$() 即調用了jQuery.fn.init方法
jQuery = function( selector, context ) { return new jQuery.fn.init( selector, context, rootjQuery ); }
下面是init方法代碼:
1 init: function( selector, context, rootjQuery ) { 2 var match, elem; 3 if ( !selector ) { 4 return this; 5 } 6 if ( typeof selector === "string" ) { 7 // code
8 } else if ( selector.nodeType ) { 9 this.context = this[0] = selector; 10 this.length = 1; 11 return this; 12 } else if ( jQuery.isFunction( selector ) ) { 13 return rootjQuery.ready( selector ); 14 } 15 if ( selector.selector !== undefined ) { 16 this.selector = selector.selector; 17 this.context = selector.context; 18 } 19 return jQuery.makeArray( selector, this ); 20 }
可以看到,里面對參數 selector 可能出現的值都做了判斷處理。
1.是否為錯誤的值,如:$(""), $(null), $(undefined), $(false)。
2.是否為字符串。
3.是否為節點元素對象。如:$(this), $(document)。
4.是否為函數。如:$(function(){})。
5.是否為jQuery對象。如:$($(element))。
6.是否為數組或json。
當參數是字符串的時候
if ( typeof selector === "string" ) { if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { match = [ null, selector, null ]; } else { match = rquickExpr.exec( selector ); } // 創建元素或者選擇ID
if ( match && (match[1] || !context) ) { // 創建元素
if ( match[1] ) { // code
// 選擇ID
} else { // code
} // 其他選擇器
} else if ( !context || context.jquery ) { return ( context || rootjQuery ).find( selector ); } else { return this.constructor( context ).find( selector ); } }
首先給變量match進行賦值
第一種情況
if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { match = [ null, selector, null ]; }
charAt方法可返回指定位置的字符。符合條件的字符串如:
"<div>", "<div></div>", "<div>111</div>"
第二種情況
match = rquickExpr.exec( selector );
rquickExpr前面已經定義過
rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,
這種語法創建了一個RegExp對象
exec方法找到匹配內容后會返回一個數組,包含表達式的完整匹配以及子表達式的匹配等,否則返回null。符合條件的字符串如:
"<div>111", "#id"
接着看后面的代碼:
if ( match && (match[1] || !context) ) { if ( match[1] ) { // code 創建元素
} else { // code 選擇ID
} }
由上面對match的賦值分析可以知道
這里面是針對創建元素和選擇id兩種情況進行處理,即我們平時這樣使用的時候:
$('<div>'), $('#id')
內部又一個if-else語句則分別是對創建元素和選擇ID進行處理
下面看處理創建元素部分:
if ( match[1] ) { context = context instanceof jQuery ? context[0] : context; jQuery.merge( this, jQuery.parseHTML( match[1], context && context.nodeType ? context.ownerDocument || context : document, true ) ); if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) { for ( match in context ) { if ( jQuery.isFunction( this[ match ] ) ) { this[ match ]( context[ match ] ); } else { this.attr( match, context[ match ] ); } } } return this; }
這里簡單介紹一下parseHTML方法和merge方法。
jQuery.parseHTML方法的作用是將字符串轉換成節點數組,如:
jQuery.merge方法的作用一般是合並數組
在這里也可以合並json
不過json的形式有一定特殊性,需要是類似這樣的:
var obj = { 0 : 'a', 1 : 'b', length : 2 }
在我們平時使用jQuery選取或創建元素后,jQuery對象中都會為節點元素創建出一個類似數組的結構,方便后續的操作。
繼續往后看還有一段代碼:
if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) { for ( match in context ) { if ( jQuery.isFunction( this[ match ] ) ) { this[ match ]( context[ match ] ); } else { this.attr( match, context[ match ] ); } } }
這段代碼的作用是給元素標簽添加屬性,如:
第一個條件
rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>|)$/,
test() 方法用於檢測一個字符串是否匹配某個模式,有則返回true,無返回false。
符合這個正則的字符串如:
"<div>", "<div></div>",也就是單標簽。
第二個條件
jQuery.isPlainObject( context ) 判斷第二個參數是否為對象字面量。
接着for...in 語句遍歷對象中的屬性,如果存在與屬性同名的方法,就調用此方法(例如上面例子中調用了html方法),否則就調用attr方法。
創建元素就到這里。接下來就是選擇id了
if ( match[1] ) { // code
} else { elem = document.getElementById( match[2] ); if ( elem && elem.parentNode ) { // Inject the element directly into the jQuery object
this.length = 1; this[0] = elem; } this.context = document; this.selector = selector; return this; }
這里就很簡單了,直接用getElementById方法獲取目標元素,然后就是對應進行賦值,創建之前說過的類似數組的結構。
下面看除了創建元素和選擇id之外的情況
if ( match && (match[1] || !context) ) { // code
} else if ( !context || context.jquery ) { return ( context || rootjQuery ).find( selector ); } else { return this.constructor( context ).find( selector ); }
可以看到,不管走后面哪條分支,最終調用的都是find方法,所以,選擇類,選擇元素和更加復雜的選擇器都並沒有在init方法中做處理,而是調用了find,這個就到后面再說。
最后我們來看一下參數除了字符串以外其他情況:
if ( typeof selector === "string" ) { // code
} else if ( selector.nodeType ) { this.context = this[0] = selector; this.length = 1; return this; } else if ( jQuery.isFunction( selector ) ) { return rootjQuery.ready( selector ); } if ( selector.selector !== undefined ) { this.selector = selector.selector; this.context = selector.context; } return jQuery.makeArray( selector, this );
當參數為元素對象或jQuery對象時,對一些屬性進行賦值。
當參數為函數時,調用了ready方法,
所以,$(function(){})這種寫法算是文檔加載$(document).ready(function(){})的一種簡寫方式。
makeArray方法類似於之前提到的merge方法,一般可以用來把節點元素轉換為數組
也可以轉換為json,同樣第二參數需要是類數組的形式
好了,總結一下, jQuery.fn.init方法對不同類型的參數進行處理,並且參數為字符串時,內部主要對創建元素和選擇id作了處理,其他情況則交給find方法,最終構造出一個關於節點元素的類數組結構以及對一些屬性的初始化。