1. 程式人生 > >jQuery原始碼分析之jQuery(selector,context)詳解

jQuery原始碼分析之jQuery(selector,context)詳解

首先我們給出下面的HTML程式碼:

<div id="parent" class="parent">
	 <div class="child">
	  child1
	</div>
	<div class="child">
	child2
	</div>
</div>
<div id="parent1" class="parent">
	 <div class="child">
	  child1
	</div>
	<div class="child">
	child2
	</div>
</div>

呼叫方式1:第二個引數context是DOM元素

var doms=$(".child",$("#parent")[0]);
console.log(doms);

這時候第二個引數是DOM物件,列印[div.child, div.child, prevObject: jQuery.fn.init[1], context: div#parent, selector: ".child"]
呼叫方式2:第二個引數context是jQuery物件

var doms=$(".child",$($("#parent")[0]));
console.log(doms);
這時候列印結果和上面第一種情況一樣,[div.child, div.child, prevObject: jQuery.fn.init[1], context: div#parent, selector: ".child"]
呼叫方式3:第二個引數是一個DOM陣列
var doms=$(".child",[document.getElementById("#parent"),document.getElementById("#parent1")]) 
console.log(doms);
呼叫方式4:第二個引數是一個jQuery物件陣列
var doms=$(".child",$(".parent")) 
console.log(doms);
這種方式的結果和第三種方式是完全一樣的!
呼叫方式5:傳入的引數是一個函式,該函式會在ready函式呼叫的時候呼叫
 $(function()
  {
    console.log("dom ready");
  })
我們現在從原始碼中分析這幾種情況:

如果傳入DOM的情況

else if ( selector.nodeType ) {
		this.context = this[0] = selector;
		this.length = 1;
		return this;
		// HANDLE: $(function)
		// Shortcut for document ready
		} 
如果傳入DOM元素,那麼直接把元素放在jQuery物件上面,同時把length自增!

如果傳入了jQuery物件

if ( selector.selector !== undefined ) {
			this.selector = selector.selector;
			this.context = selector.context;
		}
如果傳入了jQuery物件,那麼也是把引數jQuery的selector和context直接封裝到新建立的jQuery物件上面!呼叫方式如$($(''))這種方式!

如果傳入的是一個函式

 else if ( jQuery.isFunction( selector ) ) {
			return typeof rootjQuery.ready !== "undefined" ?
			rootjQuery.ready( selector ) :
				// Execute immediately if ready is not present
				selector( jQuery );
		}
如果傳入了一個函式,那麼直接放在$(document).ready()中等待執行,如果沒有ready函式那麼直接執行(使用了jQuery框架那麼ready是存在的!)
如果是傳入了一個DOM陣列或者jQuery的物件
return jQuery.makeArray( selector, this );

通過jQuery.makeArray我們可以把所有的引數封裝到一個物件上面,但是這個函式第二個引數預設是陣列物件,但是這裡傳入了jQuery物件,那麼最後返回的結果就是jQuery物件。因此,我們通過這種方式把我們傳入的DOM陣列或者jQuery物件全部封裝到一個新的jQuery物件上面返回!這種方式的呼叫如下:

 var $doms=$([document.getElementById('ql'),document.getElementById('fkl')]);
   //把上面的DOM陣列封裝到新建立的jQuery物件上
   console.log($doms);
下面這種呼叫方式是我們最常用的方式,該方式包含選擇物件的上下文:
              else if ( !context || context.jquery ) {
				return ( context || rootjQuery ).find( selector );
			// HANDLE: $(expr, context)
			// (which is just equivalent to: $(context).find(expr)
			} else {
				return this.constructor( context ).find( selector );
			}

這種方式就是如$('li',$('ul'))第二個引數如果是jQuery物件,那麼直接呼叫find方法,否則先把第二個引數的DOM物件封裝為jQuery物件然後呼叫find方法進行查詢!這種呼叫方式如下:

 var doms=$(".child",$(".parent")) ;
   //這時候我們會選擇class為parent元素下的所有的class為child的元素集合
   //作為jQuery物件返回
   console.log(doms);
很顯然,我們傳入的第二個引數是jQuery物件,那麼我們呼叫了Sizzle的find方法來進行查詢,如果第二個引數是一個DOM陣列,也會該DOM陣列封裝成為jQuery物件然後以此為上下文進行查詢!

這時候我又要提一下這種呼叫方式(第二個引數可以是DOM陣列)

var doms=$(".child",$(".parent")) 
console.log(doms);

這時候通過的就是jQuery物件具有的find例項方法來完成的(呼叫Sizzle來完成),所以返回的就是DOM陣列中每一個DOM物件的所有的滿足selector的子元素組成的集合,但是這是去重的!

之:jQuery允許我們為context傳入一個jQuery物件或者DOM陣列,表示以該DOM陣列或者jQuery物件為上下文的滿足selector的所有的元素集合,很酷的一個方法!

下面這種處理方式雖然比較少用,但是還是很不錯的:
                                 // HANDLE: $(html, props)
					if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {//<div/>或者<div></div>或<div>形式處理
						for ( match in context ) {
							// Properties of context are called as methods if possible
							if ( jQuery.isFunction( this[ match ] ) ) {//如果是內建函式
								this[ match ]( context[ match ] );//呼叫html('')
							// ...and otherwise set as attributes
							} else {
								this.attr( match, context[ match ] );//不是函式直接設定屬性!
							}
						}
					}
這種方式允許我們按照下面這種方式來建立元素的同時指定元素應該具有的屬性
$("<li>",{tile:"abcd",html:'abcn'}) 
如果是jQuery物件具有的函式,那麼我們執行,如這裡的html方法就是jQuery物件具有的內建的函式,所以我們執行他html('abcn'),傳入引數是'abcn'。而title不是內建方法,所以直接通過呼叫attr方法來為我們建立的元素指定屬性!
下面這種方式允許我們建立一個複雜的DOM元素:
jQuery.merge( this, jQuery.parseHTML(match[1],context && context.nodeType ? context.ownerDocument || context : document,true) );
而且第三個引數是true,表示我們可以建立script元素!該用法參見jQuery.parseHTML用法。

總結:

這裡只是給出了幾種我覺得應該掌握的情況,如果需要詳細瞭解,請參見init方法!