jQuery核心詳解與實踐讀書筆記2:破解jQuery選取器介面1
前兩篇已經介紹了如何搭建一個jQuery架構的雛形,從這一篇開始詳細瞭解jQuery選取器的介面。jQuery選取器功能強大但用法很簡單,它僅僅提供了一個介面:jQuery(),也可以簡寫為$()。 1. 簡單但很複雜的黑洞 jQuery提供了唯一的介面(jQuery()或$())使選取器與外界進行交流。jQuery架構的基礎是查詢,即查詢文件項目對象,因此可以認為jQuery對象就是一個選取器,並在此基礎上構建和執行查詢過濾器。jQuery查詢結果的資料集合是jQuery對象的一部分。在$()函數中可以包含選擇字串,HTML字串,DOM對象和資料集合等不同類型的參數。jQuery是如何分辨這些參數呢?為了方便理解其中的奧秘,把jQuery架構進行簡化,先刪除所有方法,函數以及邏輯代碼,然後在init()構造器中,使用alert()方法擷取selector參數的類型和資訊,代碼如下: 複製代碼 1 (function() { 2 var window = this; 3 jQuery = window.jQuery = window.$ = function(selector, context) { 4 return new jQuery.fn.init(selector, context); 5 }; 6 jQuery.fn = jQuery.prototype = { 7 init : function(selector, context) { 8 alert(selector); 9 }10 };11 })();12 window.onload = function() {13 $("div.red");//擷取"div.red"14 $("div .red");//擷取"div .red"15 $(document.getElementById("wrap"));//擷取"[object HTMLDivElement]"16 $("#wrap", document.forms[0]);//擷取"wrap"17 $("<div>hello, world</div>");//擷取"<div>hello, world</div>"18 }; 複製代碼 2. 盤根錯節的邏輯關係 jQuery()的4種構建jQuery對象的方式: jQuery(expression, [context]) jQuery(html, [ownerDocument]) jQuery(elements) jQuery(callback) jQuery對象的方法都是針對DOM元素對象進行的操作。 jQuery對象就是jQuery.fn.init構造器建立的對象,而通過jQuery.fn.init.prototype = jQuery.fn途徑,再使用jQuery的原型對象去覆蓋jQuery.fn的原型對象,使得jQuery對象的原型方法也就被繼承過來,從而形成了錯綜複雜但又井然有序的關係。 3. jQuery構造器 jQuery的構造器就是jQuery.fn.init()函數,它負責對傳入的參數進行分析,然後產生並返回jQuery對象。這個函數的第一個參數是必需的,如果為空白,則預設為document。從本質上講,使用jQuery選取器(即jQuery.fn.init()構造器)構建jQuery對象,就是在this對象上附加DOM元素集合,附加的方式包括以下兩種: 1. 如果是單個DOM元素,可以直接把DOM元素作為數組元素傳遞給this對象,還可以通過ID從DOM文檔中查詢元素 2. 如果是多個DOM元素,則以集合方式附加,如jQuery對象、數組和對象等,此時可以通過CSS選取器匹配到所有DOM元素,然後過濾,最後構建類數組的資料結構其中,CSS選取器是通過jQuery().find(selector)函數來完成的。通過jQuery().find(selector)可以分析選取器字串,並在DOM文檔樹中尋找到符合文法的元素集合。 下面先來分析一下init()初始化構造器函數,來分析jQuery選取器是如何工作的。 複製代碼 1 /** 2 * jQuery構造器源碼分析, 3 * 注意該書是以jQuery的1.3版本來分析的,與當前Github上的源碼的init函數有一定的出入, 4 * 但是整體的思路是差不多的,只是具體的實現方式有所不同而已, 5 */ 6 //jQuery原型函數,構造jQuery對象的入口 7 //所有jQuery對象方法都通過jQuery原型對象來繼承 8 jQuery.fn = jQuery.prototype = { 9 /*10 * jQuery對象初始化構造器,相當於jQuery對象的類型,有該函數負責建立jQuery對象11 * 參數說明:12 * selector:選取器的符號,可以是任意資料類型。考慮DOM元素操作需要,該函數應該是包含DOM元素的任何資料13 * context:上下文,指定在文檔DOM中哪個節點下開始進行查詢,預設值為document14 */15 init : function(selector, context) {16 selector = selector || document; //確保selector參數存在,預設值為document17 /*18 * 第一種情況,處理選擇符為DOM元素,此時將忽略上下文,即忽略第二個參數19 * 例如,$(document.getElementById("wrap")),jQuery(DOMElement)匹配DOM元素。20 * 先使用selector.nodeType判斷當selector為元素節點,將length設定為1,21 * 並且賦值給context,實際上context作為init的第二個參數,22 * 也意味著它的上下文節點就是selector該節點,返回它的$(DOMElement)對象23 */24 if(selector.nodeType) { //存在nodeType屬性,說明選擇符是一個DOM元素25 this[0] = selector; //直接把當前參數的DOM元素存入類數組中26 this.length = 1; //設定類數組的長度,以方便遍曆訪問27 this.context = selector; //設定內容屬性28 return this; //返回jQuery對象,即類數組對象29 }30 31 //如果選擇符參數為字串,則進行處理32 //例如,$("<div>hello, world</div>"),jQuery(html, [ownerDocument])匹配HTML字串33 if(typeof selector == "string") {34 //使用quickExprRegex匹配該選擇符字串,決定是處理HTML字串,還是處理ID字串35 quickExpr = /^[^<]*(<(.|\s)+>[^>]*$|^#([\w-]+)$)/;36 var match = quickExpr.exec(selector);37 //驗證匹配的資訊38 if(match && (match[1] || !context)) {39 /*40 * 第二種情況,處理HTML字串,類似$(html) -> $(array)41 */42 if(match[1]) {43 selector = jQuery.clean([match[1]], context);44 } 45 /*46 * 第三種情況,處理ID字串,類似$("#id")47 */48 else {49 var elem = document.getElementById(match[3]); //擷取該元素確保該元素存在50 //處理在IE和Opera瀏覽器下根據name,而不是根據ID返回元素51 if(elem && elem.id != match[3]) {52 return jQuery().find(selector); //預設調用document.find()方法53 }54 //否則將把elem作為元素參數直接調用jQuery()函數,返回jQuery對象55 var ret = jQuery(elem || []); 56 ret.context = document; //設定jQuery對象的內容屬性57 ret.selector = selector; //設定jQuery對象上的選擇符屬性58 return ret; //返回jQuery對象59 }60 } 61 /*62 * 第四種情況,處理jQuery(expression, [context])63 * 例如,$("div .red")的運算式字串64 */65 else {66 return jQuery(context).find(selector);67 }68 } else if(jQuery.isFunction(selector)) {69 /*70 * 第五種情況,處理jQuery(callback),即$(document).ready()的簡寫71 * 例如,$(function() { alert("hello, world");}),72 * 或者 $(document).ready(function() { alert("hello, world");})73 */74 return jQuery(document).ready(selector);75 }76 //確保舊的選擇符能夠通過77 if(selector.selector && selector.context) {78 this.selector = selector.selector;79 this.context = selector.context;80 }81 /*82 * 第六種情況,處理類似$(elements)83 */84 return this.setArray(jQuery.isArray(selector) ? selector : jQuery.makeArray(selector));85 }86 //其它代碼。。。87 };複製代碼代碼中已經對相關的判斷邏輯做了詳細的說明,這裡就不在做解釋說明了。 今天就先寫到這裡,下一篇內容將是關於jQuery如何產生DOM元素和引用DOM元素。歡迎轉載,轉載請註明出處。