摘要:承接第一篇末尾內容,本部分開始進入主模塊,分析其設計思路與實現技巧下文代碼均進行過重格式化,但代碼版本同第一部分內容且入口函數不變的選擇器先從第一個與原型鏈構造不直接相關的工具函數說起,觀察的設計思路。
承接第一篇末尾內容,本部分開始進入 zepto 主模塊,分析其設計思路與實現技巧(下文代碼均進行過重格式化,但代碼 Commit 版本同第一部分內容且入口函數不變):
Zepto 的選擇器 zepto.qsa()// Line 262 zepto.qsa = function(element, selector) { };
先從第一個與原型鏈構造不直接相關的工具函數 qsa 說起,觀察 Zepto 的設計思路。
// Line 28 simpleSelectorRE = /^[w-]*$/, // Line 337 var found, maybeID = selector[0] == "#", maybeClass = !maybeID && selector[0] == ".", nameOnly = maybeID || maybeClass ? selector.slice(1) : selector, // Ensure that a 1 char tag name still gets checked isSimple = simpleSelectorRE.test(nameOnly);
函數開始部分先定義了幾個 Bool 值,用以猜測是否可能為 id 或 class,此時如果可能是兩者中的一個,那么去除標記部分(. or #),否則取自身記為 nameOnly。simpleSelectorRE 用于測試可能被剝離了一次標記部分的 selector 是否滿足是一般字符串的要求,如果不是,那么可能查詢目標是多個條件組合(如 .class1.class2),后面直接放入原生的 querySelectorAll 方法查詢。
// Line 268 return element.getElementById && isSimple && maybeID // Safari DocumentFragment doesn"t have getElementById ? (found = element.getElementById(nameOnly)) ? [found] : []
進入包含一系列判斷的 return 階段,268 行中出現了一個兼容性注釋,由于前方的 maybeClass 定義中聲明了并非 id 所以此處不支持 getElementById 方法也將直接陷入原生的 querySelectorAll 方法。如果滿足查詢條件則發給原生 getElementById` 方法查詢,返回數組方式的結果。
// Line 6 var undefined, key, $, classList, emptyArray = [], concat = emptyArray.concat, filter = emptyArray.filter, slice = emptyArray.slice, // Line 270 : element.nodeType !== 1 && element.nodeType !== 9 && element.nodeType !== 11 ? [] : slice.call( isSimple && !maybeID && element.getElementsByClassName // DocumentFragment doesn"t have getElementsByClassName/TagName ? maybeClass ? element.getElementsByClassName(nameOnly) // If it"s simple, it could be a class : element.getElementsByTagName(selector) // Or a tag : element.querySelectorAll(selector) // Or it"s not simple, and we need to query all );
先參照 nodeType 判斷了根搜索元素類型,此處采用了和 id 相同的降級策略,并通過調用空數組上方法的方式調用了 Array.prototype 上的 slice 方法完成數組生成,整體 Zepto 庫實際上使用了相同的思想利用原型鏈給予 Z 對象上的操作方法。
Zepto 的幾個工具函數設計Zepto 的數組與對象相關工具函數較相似于 Underscore.js 先行略去,著重列舉幾個有技巧的實現:
類型相關工具函數的例子:
// Line 29 class2type = {}, toString = class2type.toString, // Line 401 // Populate the class2type map $.each( "Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) { class2type["[object " + name + "]"] = name.toLowerCase(); } ); // Line 65 function type(obj) { return obj == null ? String(obj) : class2type[toString.call(obj)] || "object" }
工具函數 type 中出現了 == 運算符,此處利用了 null/undefined == null 的語言特性,并通過 String 包裝類進行類型轉換得到其類型的字符串表示,如果并非為這兩種類型,則通過 class2type 的映射關系將其轉化為對應的字符串類型名。
// Line 78 function likeArray(obj) { var length = !!obj && "length" in obj && obj.length, type = $.type(obj) return "function" != type && !isWindow(obj) && ( "array" == type || length === 0 || (typeof length == "number" && length > 0 && (length - 1) in obj) ) }
工具函數 likeArray 實際上給出了 Zepto 所認為的數組形式,即:存在正 length 的 Number 型成員變量及 Key 值為 length - 1 的成員變量且并非是函數的對象。這樣定義可以使得迭代器模式可以使用,且恰好使用了未初始化的數組項為 undefined 類型的語言屬性。
判定元素與選擇器匹配性的函數 matches
與 qsa() 函數類似,Zepto 還給出了一個類型匹配函數 zepto.matches() 用于判斷某個元素是否與一個給定的選擇器匹配:
// Line 33 tempParent = document.createElement("div"), // Line 51 zepto.matches = function(element, selector) { // 如果不滿足匹配的類型條件,那么返回結果為 False if (!selector || !element || element.nodeType !== 1) return false; // Element.prototype.matches() - 判定某個元素是否符合某個選擇器 // https://dom.spec.whatwg.org/#dom-element-matches var matchesSelector = element.matches || element.webkitMatchesSelector || element.mozMatchesSelector || element.oMatchesSelector || element.matchesSelector; if (matchesSelector) return matchesSelector.call(element, selector); // 如果當前瀏覽器未實現 matches API,則降級為使用 qsa 函數完成 // 如果父節點存在,則選取父節點進行 qsa() // 如果父節點不存在,將目標節點放入預定的父節點中,再在父節點上進行 qsa() 檢驗是否可以找到子節點 // fall back to performing a selector: var match, parent = element.parentNode, temp = !parent; if (temp) (parent = tempParent).appendChild(element); match = ~zepto.qsa(parent, selector).indexOf(element); // 清除可能創建的父節點 temp && tempParent.removeChild(element); return match; };
相似的構造父級容器以查詢子級元素性質思路在 Zepto 源代碼中多次出現,例如對于另一個工具函數 defaultDisplay 的實現中。
獲取當前瀏覽器下某元素默認 display 值的 defaultDisplay() 函數,由于 DOM 中的元素默認樣式值實際上在用戶進行更改前即為瀏覽器賦予節點類型的默認值,因此查詢元素的默認值可以變為查詢某節點類型的默認值:
// Line 8 elementDisplay = {} // Line 109 function defaultDisplay(nodeName) { var element, display; // 如果全局 elementDisplay 對象中已經緩存了查詢目標 nodeName 的結果那么直接查詢,否則陷入邏輯 if (!elementDisplay[nodeName]) { // 創建一個同類型節點,將其放入 body 下獲取它的實時計算值中的 display 屬性 element = document.createElement(nodeName); document.body.appendChild(element); // 此處引用了 IE 模塊中的 getComputedStyle() 函數降級 display = getComputedStyle(element, "").getPropertyValue("display"); // 刪除用于取值的元素對象,如果元素的 display 值為 none 那么將其值設為 block // 此處將 none 置為 display 的原因為 $.fn.show() 函數中通過該函數獲取一個非隱藏型的默認值 element.parentNode.removeChild(element); display == "none" && (display = "block"); // 緩存結果值至全局變量 elementDisplay elementDisplay[nodeName] = display; } return elementDisplay[nodeName]; } // Line 574 show: function() { return this.each(function() { this.style.display == "none" && (this.style.display = ""); // defaultDisplay() 獲取值為 none 時設定為 block 的原因 if (getComputedStyle(this, "").getPropertyValue("display") == "none") this.style.display = defaultDisplay(this.nodeName); }); },Zepto 加載擴展的方法
本節末尾,簡單介紹一下擴展 Zepto 的方法。在主模塊 Zepto 外,一個未默認編譯的模塊 Selector 包含了擴展原 qsa() 函數的實現,進入模塊代碼 src/selector.js,其結構如下:
(function($) { var zepto = $.zepto, oldQsa = zepto.qsa, oldMatches = zepto.matches; zepto.qsa = function(node, selector) { // 擴展的 zepto.qsa 實現 }; zepto.matches = function(node, selector) { // 擴展的 zepto.matches 實現 }; })(Zepto);
在實際編譯中只需將 Selector 在核心模塊后編譯即可替換原始的 qsa 函數與對應的 matches 函數,因此基于該思路的 Zepto 外掛模塊非常簡單。在分析核心模塊邏輯時,可以通過此方法改寫函數,或者嘗試基于業務需求配置一個新的數據結構,再利用 Zepto 實現對 DOM 的增刪改查。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/108723.html
摘要:如果偽類的參數不可以用轉換,則參數為字符串,用正則將字符串前后的或去掉,再賦值給最后執行回調,將解釋出來的參數傳入回調函數中,將執行結果返回。重寫的方法,改過的調用的是方法,在回調函數中處理大部分邏輯。 Selector 模塊是對 Zepto 選擇器的擴展,使得 Zepto 選擇器也可以支持部分 CSS3 選擇器和 eq 等 Zepto 定義的選擇器。 在閱讀本篇文章之前,最好先閱讀《...
摘要:方法是將集合中不符合條件的元素查找出來。判斷集合中的第一個元素是否匹配指定的選擇器。這個在讀源碼之集合操作有講過,如果集合個數大于零,則表示滿足條件。返回集合中所有元素指定的屬性值。獲取集合中每個元素的前一個兄弟節點。 這篇依然是跟 dom 相關的方法,側重點是跟集合元素查找相關的方法。 讀Zepto源碼系列文章已經放到了github上,歡迎star: reading-zepto 源碼...
摘要:源碼分析一核心代碼分析源碼分析二奇淫技巧總結本文只分析核心的部分代碼,并且在這部分代碼有刪減,但是不影響代碼的正常運行。當長度為則不添加內容,否則逐個將逐個到當前實例新增直接返回一個新的構造函數添加初始化方法。 Zepto源碼分析(一)核心代碼分析Zepto源碼分析(二)奇淫技巧總結 本文只分析核心的部分代碼,并且在這部分代碼有刪減,但是不影響代碼的正常運行。 目錄 * 用閉包封裝Z...
摘要:選擇的理由是一個用于現代瀏覽器的與大體兼容的庫。環境搭建分析環境的搭建僅需要一個常規頁面和原始代碼一個常規頁面打開的首頁即可,在開發人員工具中即可使用原始代碼本篇分析的代碼參照,進入該代碼分支中即可。 選擇 Zepto 的理由 Zepto is a minimalist JavaScript library for modern browsers with a largely jQue...
摘要:返回值為,如果能查找到元素,則將元素以數組的形式返回,否則返回空數組排除不合法的。的第一個字符為,并且為標簽。如果存在,則查找下選擇器為的所有子元素。正則表達式為如果沒有指定標簽名,則獲取標簽名。包裹元素的即為所需要獲取的。 經過前面三章的鋪墊,這篇終于寫到了戲肉。在用 zepto 時,肯定離不開這個神奇的 $ 符號,這篇文章將會看看 zepto 是如何實現 $ 的。 讀Zepto源碼...
閱讀 3813·2021-10-12 10:11
閱讀 3637·2021-09-13 10:27
閱讀 2539·2019-08-30 15:53
閱讀 1971·2019-08-29 18:33
閱讀 2189·2019-08-29 14:03
閱讀 993·2019-08-29 13:27
閱讀 3315·2019-08-28 18:07
閱讀 763·2019-08-26 13:23