一、源碼思路分析總結
[ 作者:華子yjh ]
概要:
jQuery的核心思想可以簡單概括為“查詢和操作dom”,今天主要是分析一下jQuery.prototype.init選擇器構造函數,處理選擇器函數中的參數;
這個函數的參數就是jQuery()===$()執行函數中的參數,可以先看我之前寫的淺析jQuery基礎框架一文,了解基礎框架后,再看此文。
思路分析:
以下是幾種jQuery的使用情況(用於查詢dom),每種情況都返回一個選擇器實例(習慣稱jQuery對象(一個nodeList對象),該對象包含查詢的dom節點):
1、處理 $(""), $(null), $(undefined), $(false)
如果參數為以上非法值,jQuery對象不包含dom節點
2、處理 $(DOMElement)
如果參數為節點元素,jQuery對象包含該參數節點元素,並分別增加屬性值為參數節點元素、1的context、length屬性和用[]訪問jQuery對象中dom節點的用法
例2.1:
1 var obj = document.getElementById('container'), 2 jq = $(obj); 3 4 console.log(jq.length); //1 5 console.log(jq.context); //obj 6 console.log(jq.[0]); //obj
3、處理$(HTML字符串)
如果第一個參數為HTML字符串,jQuery對象包含由jQuery.clean函數創建的fragment文檔碎片中的childnodes節點
例3.1:
1 var jqHTML = $('<h1>文章標題</h1><p>內容</p>'); 2 console.log(jqHTML); //[<h1>,<p>];
如果第一個參數(HTML字符串)為一個空的單標簽,且第二個參數context為一個非空純對象
例3.2:
1 var jqHTML = $('<div></div>', { class: 'css-class', data-name: 'data-val' }); 2 3 console.log(jqHTML.attr['class']); //css-class 4 console.log(jqHTML.attr['data-name']); //data-val
4、處理$(#id)
如果第一個參數是一個#加元素id,jQuery對象包含唯一擁有該id的元素節點,
並分別增加屬性值為document、參數字符串、1、的context、selector、length屬性和用[]訪問jQuery對象中dom節點的用法
例4.1:
1 var jq = $('#container'); 2 3 console.log(jq.[0]); //包含的dom節點元素 4 console.log(jq.length); //1 5 console.log(jq.context); //document 6 console.log(jq.selector); //container
5、處理$(.className)
如果第一個參數是一個.className,jQuery對象中擁有class名為className的標簽元素,並增加一個屬性值為參數字符串、document的selector、context屬性
實際執行代碼為:
1 return jQuery(document).find(className);
6、處理$(.className, context)
如果第一個參數是.className,第二個參數是一個上下文對象(可以是.className(等同於處理$(.className .className)),jQuery對象或dom節點),
jQuery對象包含第二個參數上下文對象中擁有class名為className的后代節點元素,並增加一個context和selector屬性
實際執行代碼為:
1 return jQuery(context).find(className);
例6.1:
html代碼:
1 <div class="main"> 2 <h2 class="title">主內容標題</h2> 3 <p>主標題</p> 4 </div> 5 6 <div class="sub"> 7 <h2 class="title">次內容標題</h2> 8 <p>次標題</p> 9 </div>
JavaScript代碼:
1 var jq, context; 2 3 context = '.sub'; 4 var jq = $('.title', context); 5 console.log(jq.text()); //次內容標題 6 console.log(jq.context); //document 7 console.log(jq.selector); //.sub .title 8 9 context = $('.sub'); 10 var jq = $('.title', context); 11 console.log(jq.text()); //次內容標題 12 console.log(jq.context); //document 13 console.log(jq.selector); //.sub .title 14 15 context = $('.sub')[0]; 16 var jq = $('.title', context); 17 console.log(jq.text()); //次內容標題 18 console.log(jq.context); //className為sub的節點元素 19 console.log(jq.selector); //.title
7、處理$(fn)
如果第一個參數是fn函數,則調用$(document).ready(fn);
例7.1:
1 $(function(e){ 2 console.log('DOMContent is loaded'); 3 }) 4 //上面代碼等同於: 5 jQuery(document).ready(function(e) { 6 console.log('DOMContent is loaded'); 7 });
8、處理$(jQuery對象)
如果第一個參數是jQuery對象,上面已經分析過如果在查詢dom時,參數是一個#加元素id,返回的jQuery對象會增加一個屬性值為參數字符串、document的selector、context屬性
例8.1:
1 var jq = $('#container'); 2 console.log(jq.selector); // #container 3 console.log(jq.context); // document
那么當出現$($('#container'))該如何處理呢?同樣的,返回的jQuery對象同情況5和6處理的情況一樣
例8.2:
1 var jq2 = $($('#container')); 2 console.log(jq2.selector); // #container 3 console.log(jq2.context); // document
二、源碼注釋分析
[ 基於jQuery1.8.3 ]
1 var rootjQuery = $(document), 2 rquickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/; 3 4 jQuery.fn = jQuery.prototype = { 5 init: function( selector, context, rootjQuery ) { 6 var match, elem, ret, doc; 7 8 // Handle $(""), $(null), $(undefined), $(false) 9 if ( !selector ) { 10 return this; 11 } 12 13 // Handle $(DOMElement) 14 if ( selector.nodeType ) { 15 this.context = this[0] = selector; 16 this.length = 1; 17 return this; 18 } 19 20 // Handle HTML strings 21 if ( typeof selector === "string" ) { 22 if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { 23 // Assume that strings that start and end with <> are HTML and skip the regex check 24 match = [ null, selector, null ]; 25 26 } else { 27 match = rquickExpr.exec( selector ); 28 } 29 30 // Match html or make sure no context is specified for #id 31 // match[1]不為null,則為html字符串,match[2]不為null,則為元素id 32 if ( match && (match[1] || !context) ) { 33 34 // HANDLE: $(html) -> $(array) 35 if ( match[1] ) { 36 context = context instanceof jQuery ? context[0] : context; 37 doc = ( context && context.nodeType ? context.ownerDocument || context : document ); 38 39 // scripts is true for back-compat 40 // selector是由文檔碎片中的childnodes組成的數組 41 selector = jQuery.parseHTML( match[1], doc, true ); 42 43 // 如果match[1]為空的單標簽元素(如:<div><div>)且context為對象字面量 44 if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) { 45 46 // 如果context對象不為空,則將對象中的屬性添加到selector數組中僅有的dom節點中 47 this.attr.call( selector, context, true ); 48 } 49 50 // merge函數的參數應該為兩個數組,目的是將第二個數組中的項合並到第一個數組,而this並不是一個數組, 51 // this是選擇器init構造函數的實例對象,該對象繼承jQuery.prototype對象中的length屬性(默認為0),因此可以理解好merge函數源碼 52 // 將selector中的dom項合並到this對象中,並返回該對象 53 return jQuery.merge( this, selector ); 54 55 // HANDLE: $(#id) 56 } else { 57 elem = document.getElementById( match[2] ); 58 59 // Check parentNode to catch when Blackberry 4.6 returns 60 // nodes that are no longer in the document #6963 61 if ( elem && elem.parentNode ) { 62 // Handle the case where IE and Opera return items 63 // by name instead of ID 64 // ie6,7和Opera存在此bug,當一個標簽name和一個標簽id值相等時, 65 // document.getElementById(#id)函數將返回提前出現的標簽元素 66 if ( elem.id !== match[2] ) { 67 // 如果存在以上Bug,則返回由find函數返回的document文檔的后代元素集合 68 return rootjQuery.find( selector ); 69 } 70 71 // Otherwise, we inject the element directly into the jQuery object 72 this.length = 1; 73 this[0] = elem; 74 } 75 76 this.context = document; 77 this.selector = selector; 78 return this; 79 } 80 81 // HANDLE: $(expr, $(...)) 82 // context不存在或者context為jQuery對象 83 } else if ( !context || context.jquery ) { 84 return ( context || rootjQuery ).find( selector ); 85 86 // HANDLE: $(expr, context) 87 // (which is just equivalent to: $(context).find(expr) 88 // context為className或者dom節點元素 89 } else { 90 // 等同於jQuery(context).find(selector) 91 return this.constructor( context ).find( selector ); 92 } 93 94 // 處理$(fn)===$(document).ready(fn) 95 } else if ( jQuery.isFunction( selector ) ) { 96 return rootjQuery.ready( selector ); 97 } 98 99 // 處理$(jQuery對象) 100 if ( selector.selector !== undefined ) { 101 this.selector = selector.selector; 102 this.context = selector.context; 103 } 104 105 // 當第一個參數selector為jQuery對象時,將selector中的dom節點合並到this對象中,並返回this對象 106 return jQuery.makeArray( selector, this ); 107 } 108 }