摘要:對于操作來說,是非常方便的一個庫,雖然如今隨著之類框架的流行,用得越來越少了,但是其中很多思想還是非常值得我們學(xué)習(xí)的,這篇文章將介紹如何從零開始實現(xiàn)一個簡化版。要實現(xiàn)類似這樣的鏈?zhǔn)讲僮鳎恍枰诿總€實例方法中返回一個即可。
對于操作 DOM 來說,jQuery 是非常方便的一個庫,雖然如今隨著 React, Vue 之類框架的流行,jQuery 用得越來越少了,但是其中很多思想還是非常值得我們學(xué)習(xí)的,這篇文章將介紹如何從零開始實現(xiàn)一個簡化版 jQuery。
在這里,我把這個庫命名為 Clus(class 的諧音),下面以 $ 符號代替。
首先需要聲明一個構(gòu)造函數(shù)并做一些初始化操作:
function $(selector) { return new $.fn.init(selector); } $.fn = $.prototype = { contructor: $, init, };
可以看到,該構(gòu)造函數(shù)返回一個 $.fn.init 的實例,這樣做的好處就是在使用的時候不要每次都 new 一個構(gòu)造函數(shù)就可以創(chuàng)建一個新的實例了,可以看出來,整個核心都在 init 函數(shù)上了:
function init(selector) { const fragmentRE = /^s*<(w+|!)[^>]*>/; const selectorType = $.type(selector); const elementTypes = [1, 9, 11]; let dom; if (!selector) { dom = [], dom.selector = selector; } else if (elementTypes.indexOf(selector.nodeType) !== -1 || selector === window) { dom = [selector], selector = null; } else if (selectorType === "function") { return $(document).ready(selector); } else if (selectorType === "array") { dom = selector; } else if (selectorType === "object") { dom = [selector], selector = null; } else if (selectorType === "string") { if (selector[0] === "<" && fragmentRE.test(selector)) { dom = $.parseHTML(selector), selector = null; } else { dom = [].slice.call(document.querySelectorAll(selector)); } } dom = dom || []; $.extend(dom, $.fn); dom.selector = selector; return dom; }
可以很清楚的看到,根據(jù)傳入的參數(shù)類型的不同進行一些不同的操作,比如傳入的是函數(shù)的話,則該函數(shù)里的操作的都是 DOM Ready 之后的操作了;再比如傳入的是字符串的話,并且如果是標(biāo)簽的話,則會把這段標(biāo)簽字符串解析成 DOM Fragment,如果是普通字符串,則會調(diào)用 document.querySelectorAll() 方法來查找 DOM。
相信大家都能很容易的看明白上面的代碼,不過有一點值得一提的是 $.extend(dom, $.fn); 這段代碼,其含義是把實例上的所有方法都添加到 dom 這個數(shù)組對象中,這樣做的目的就是為了可以直接鏈?zhǔn)秸{(diào)用某個實例的方法,比如 $(".clus").addClass("hello"),這個 addClass() 方法就是在 $.fn 上實現(xiàn)的。因此所有在 $.fn 實現(xiàn)的方法都可以通過 $(selector).method() 這種方式來調(diào)用了。
至于 extend() 方法我認(rèn)為是除了 init() 方法以外,整個庫中最核心的一個方法了,代碼如下:
export default function extend() { let options, name, clone, copy, source, copyIsArray, target = arguments[0] || {}, i = 1, length = arguments.length, deep = false; if (typeof target === "boolean") { deep = target; target = arguments[i] || {}; i++; } if (typeof target !== "object" && $.type(target) !== "function") { target = {}; } if (i === length) { target = this; i--; } for (; i < length; i++) { // if ((options = arguments[i]) !== null) { // for in source object for (name in options) { source = target[name]; copy = options[name]; if (target == copy) { continue; } // deep clone if (deep && copy && ($.isPlainObject(copy) || (copyIsArray = Array.isArray(copy)))) { // if copy is array if (copyIsArray) { copyIsArray = false; // if is not array, set it to array clone = source && Array.isArray(source) ? source : []; } else { // if copy is not a object, set it to object clone = source && $.isPlainObject(source) ? source : {}; } target[name] = extend(deep, clone, copy); } else if (copy !== undefined) { target[name] = copy; } } } } return target; }
可以看到,和 jQuery 的實現(xiàn)一毛一樣,沒錯就是從那兒 copy 過來的當(dāng)然一樣。
下面以 addClass() 方法為例介紹如何操作 DOM 的:
function addClass(cls) { let classes, clazz, el, cur, curValue, finalValue, j, i = 0; if (typeof cls === "string" && cls) { classes = cls.match(rnotwhite) || []; while((el = this[i++])) { curValue = getClass(el); cur = (el.nodeType === 1) && ` ${curValue} `.replace(rclass, " "); if (cur) { j = 0; while((clazz = classes[j++])) { // to determine whether the class that to add has already existed if (cur.indexOf(` ${clazz} `) == -1) { cur += clazz + " "; } finalValue = $.trim(cur); if ( curValue !== finalValue ) { el.setAttribute("class", finalValue); } } } } } return this; } $.fn.addClass = addClass;
值得一提的就是在實例方法中,this 關(guān)鍵字可以訪問到根據(jù)選擇器所查詢到的所有元素的集合,在這里是通過 while 循環(huán)來對每個元素進行操作。要實現(xiàn)類似 $(selector).addClass().removeClass() 這樣的鏈?zhǔn)讲僮鳎恍枰诿總€實例方法中返回一個 this 即可。要實現(xiàn)其他實例方法比如 hasClass() 之類的也是類似的方法。
其實每個實例方法都是通過 this 關(guān)鍵字來獲取查詢到的元素,然后遍歷這些元素來針對每個元素進行具體的操作,在舉一個栗子:
function append(DOMString) { let el, i = 0, fregmentCollection = $.parseHTML(DOMString), fregments = Array.prototype.slice.apply(fregmentCollection); while((el = this[i++])) { fregments.map(fregment => { el.appendChild(fregment); }); } return this; } $.fn.append = append;
上面是 append() 的實現(xiàn),首先先解析 DOMString 為 fregment,然后就是遍歷查詢到的元素(通過 this 關(guān)鍵字)并針對每個元素去進行 appendChild() 的操作,從而把 DOM 插入到匹配到的所有元素中。
其他實例方法也是通過類似的方式實現(xiàn)的,這里就不一一細(xì)說了,想更詳細(xì)的查看其他方法的實現(xiàn)可以直接到 Clus 中查看源碼。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/100655.html
摘要:而在構(gòu)造函數(shù)中,返回了的實例對象。在中直接返回過的實例,這里的是的真正構(gòu)造函數(shù)最后對外暴露入口時,將字符與對等起來。因此當(dāng)我們直接使用創(chuàng)建一個對象時,實際上是創(chuàng)建了一個的實例,這里的正真構(gòu)造函數(shù)是原型中的方法。 showImg(https://segmentfault.com/img/remote/1460000008749398); 早幾年學(xué)習(xí)前端,大家都非常熱衷于研究jQuery源...
摘要:無論是還是,對許多應(yīng)用來說,這個大小都是完全可以接受的。但是為了不用,放棄向后兼容那我覺得還是用得了。自從年雙十一正式上線,累計處理了億錯誤事件,付費客戶有金山軟件百姓網(wǎng)等眾多品牌企業(yè)。 譯者按: 看來 jQuery 還是有一些用武之地的。 原文: Why Im Still Using jQuery in 2019 譯者: Fundebug 為了保證可讀性,本文采用意譯而非直譯...
摘要:而且已開源出來,隨著容器技術(shù)發(fā)展,大文件分發(fā)一直是個重要的問題,所以是一件值得研究的技術(shù)。實用推薦檢定攻略是近期推出的一項認(rèn)證,用以認(rèn)證開發(fā)者的移動網(wǎng)頁開發(fā)技能。凈化,移除中不必要的文件技術(shù)周刊由小組出品,匯聚一周好文章,周刊原文。 業(yè)界動態(tài) 直擊阿里雙11神秘技術(shù):PB級大規(guī)模文件分發(fā)系統(tǒng)蜻蜓 文章主要介紹了阿里的PB級大規(guī)模文件分發(fā)系統(tǒng)蜻蜓, 通過使用P2P技術(shù)同時結(jié)合智能壓縮、智...
摘要:官網(wǎng)源碼推薦從開始手寫一個框架更多請在技術(shù)棧微信公眾號后臺回復(fù)關(guān)鍵字。是一個開放源代碼的對象關(guān)系映射框架,它對進行了非常輕量級的對象封裝,它將與數(shù)據(jù)庫表建立映射關(guān)系,是一個全自動的框架。 Java 程序員方向太多,且不說移動開發(fā)、大數(shù)據(jù)、區(qū)塊鏈、人工智能這些,大部分 Java 程序員都是 Java Web/后端開發(fā)。那作為一名 Java Web 開發(fā)程序員必須需要熟悉哪些框架呢? 今天...
摘要:在內(nèi)部還是調(diào)用這些方法。對象下標(biāo),從開始對象下標(biāo),從開始再次重申對象只能調(diào)用對象的,對象只能調(diào)用對象的對象轉(zhuǎn)成值得注意的是在腳本內(nèi),是代表對象的。對象轉(zhuǎn)成對象語法也非常簡單在內(nèi)寫上對象,就變成了對象了。在文檔中對它的解釋是這樣子的。 什么是Jquery? Jquey就是一款跨主流瀏覽器的JavaScript庫,簡化JavaScript對HTML操作 就是封裝了JavaScript,能夠...
閱讀 2696·2023-04-25 21:26
閱讀 1514·2021-11-25 09:43
閱讀 1949·2019-08-30 15:52
閱讀 932·2019-08-30 14:05
閱讀 2614·2019-08-29 16:10
閱讀 414·2019-08-29 13:48
閱讀 1860·2019-08-29 12:47
閱讀 1299·2019-08-23 18:04