天天看點

jQuery源碼閱讀(六)---jQuery執行個體方法解析

前面基本将jQuery入口子產品中的jQuery建立(init函數)搞清楚了,在jQuery源碼中,接下來是對一些jQuery執行個體對象方法的定義。

比如

size                     //傳回對象中元素個數
toArray()                //将jQuery對象轉換為數組
get()                   //擷取到jQuery對象中對應位置的元素
pushStack()             //模拟的棧
each()                  //類似于for循環,對每個元素進行操作
ready()                 //DOM加載完成時觸發的函數
first()                 //擷取jQuery對象(集)的第一個元素
last()                  //擷取jQuery對象(集)中的最後一個元素
eq(num)                    //擷取jQuery對象(集)中的第num個元素
end()                   //用于回溯
map()                   //類似于數組的進階方法map(),即對應元素對應操作
push()                 //類似于數組的push
slice()                //類似于數組方法
splice()               //類似于數組方法
//後面這三種方法隻是供内部使用的
           

下來對于一些重要函數或比較難了解的函數做一分析。

pushStack方法

首先我們得知道這個函數是幹嘛的?

舉個例子:

$('div').pushStack($('ul'));
           
jQuery源碼閱讀(六)---jQuery執行個體方法解析

·

可以看到,最終得到的元素是ul,并且其jQuery對象有一個prevObject屬性,也是一個對象$(‘div’)。

從這不難看出,這類似與一個棧的結構

jQuery源碼閱讀(六)---jQuery執行個體方法解析

目前能得到的是棧頂的

$('ul')

,而

$('ul')

的prevObject是

$('div')

。那麼為什麼要這麼做呢?

之前在慕課網上看到過說,jQuery中有回溯的概念,而且我們也經常會用到的,就是end()函數。

比如:

//所有的li元素設border
$('ul').children().css('border', '1px solid black');  
           
jQuery源碼閱讀(六)---jQuery執行個體方法解析
//所有的li元素設border和背景顔色          
$('ul').children().css('border', '1px solid black').css('background-color', 'pink')    
           
jQuery源碼閱讀(六)---jQuery執行個體方法解析
//li元素設border,ul 元素設背景顔色
$('ul').children().css('border', '1px solid black').end().css('border', '1px solid red') 
           
jQuery源碼閱讀(六)---jQuery執行個體方法解析
//li元素設border,ul 元素設背景顔色       
$('ul').children().css('border', '1px solid black').end().css('background-color', 'blue')   
           
jQuery源碼閱讀(六)---jQuery執行個體方法解析

可以看到end()函數相當于彈棧(出棧)的作用,傳回的是棧頂元素的底下的那個元素。

它的源碼也很簡單:(就是傳回目前元素的prevObject)

return this.prevObject || this.constructor(null);
           

而end()函數的實作完全是基于pushStack的,沒有pushStack構造好這個資料結構,那麼end()也不可能這麼容易就實作。

下來我們看一下pushStack是如何來實作的。記住:類似于棧,那麼原理上就是後進先出的原則。

pushStack : function( elems, name, selector ){
    // 建立一個空的jQuery對象
    var ret = this.constructor();

    //将要壓棧的元素存到ret裡面,變成一個數組
    if ( jQuery.isArray( elems ) ) {
        push.apply( ret, elems );
    } else {
        jQuery.merge( ret, elems );
    }

    //将上一個對象設為ret的prevObject對象
    ret.prevObject = this;

    ret.context = this.context;

    //暫時不了解name的作用.
    if ( name === "find" ) {
        ret.selector = this.selector + ( this.selector ? " " : "" ) + selector;
    } else if ( name ) {
        ret.selector = this.selector + "." + name + "(" + selector + ")";
    }

    // Return the newly-formed element set
    return ret;
}
           

直覺上看,pushStack就做了這麼一件事:

jQuery源碼閱讀(六)---jQuery執行個體方法解析

其實依賴pushStack的函數很多,像上面提到的一些方法:

toArray()                //将jQuery對象轉換為數組
eq(num)                    //擷取jQuery對象(集)中的第num個元素
end()                   //用于回溯
map()                   //類似于數組的進階方法map(),即對應元素對應操作
slice()                //類似于數組方法
           

toArray方法

//調slice方法,類似于數組,它是對數組截取某一段的方法,傳回新數組
return slice.call( this,  );
           

slice方法

舉個例子:

$('li').slice(,)
           

這裡對選擇到的兩個li元素進行截取,隻取第一個li,可以看到slice傳回的結果

jQuery源碼閱讀(六)---jQuery執行個體方法解析

結果中得到的對象,有prevObject屬性,還有selector,context,可以想像得到該方法也是調用了pushStack方法。具體源碼如下:

return this.pushStack( slice.apply( this, arguments ),
            "slice", slice.call(arguments).join(",") );
           

看到這,貌似可以了解pushStack源碼中的name和傳回的selector的含義了,可能這隻是一個辨別,其實真正沒有實際的作用。

在slice源碼裡面,先是利用數組原生的slice方法去分割,注意apply 方法的用途,改變函數作用域。 然後再把分割得到的元素(集)壓入棧,便于回溯。也就是說,加入想通過鍊直接對所有的元素進行操作時,不用重新去擷取,直接調end()函數回溯就可以了,這樣的好處是對性能不會有壞的影響。而如果重新去擷取,相當于又建立了jQuery對象,如果很多這種操作,對記憶體需求是很大的,必然會影響性能。

eq(num)方法

取對象(集)或數組中的第num個元素。

eq: function(i){
    i = +i;
    return i === - ?
    this.slice( i ) :
    this.slice( i, i +  );
}   //1.7.2版本調的是slice方法,而2.0版本調的是pushStack方法,并直接取this[i]
           

map()方法

在應用層的用法:

jQuery源碼閱讀(六)---jQuery執行個體方法解析

可以看到,傳回的是一個新的對象,并且也是有prevObject屬性。并且map裡面的回調函數對于選中的每一個元素都進行了操作。

源碼:

map: function( callback ) {  
    //在源碼中,map函數調了底層的jQuery.map方法。
    return this.pushStack( jQuery.map(this, function( elem, i ) {
        return callback.call( elem, i, elem );
    }));
}
           

上面這些方法都是在jQuery對象上的方法,之是以能夠共享,是因為

jQuery.fn = jQuery.prototype = jQuery.fn.init.prototype

這是非常重要的一點。今天就先整理這麼多吧,後面待續。。。。

繼續閱讀