jQuery源碼學習 item2-jQuery對象及其屬性和方法

一、jQuery函數

先回顧一下我們常用的jQuery格式,例如我們需要設置一個div的css樣式,那麼會寫成如下的格式:$(“#btn”).css(),就有點類似於一個對象調用自己的方法,$(“#btn”)是一個執行函數,在文檔的61行:

這裏寫圖片描述

我們可以發現在63行,函數返回的是一個通過new創建的對象,這也是jQuery的巧妙之處,不通過new構造函來創建對象。這裏具體分析一下。

一般如果我們聽過構造函數來創建的話,會寫成如下格式:

function jquery(){} //創建構造函數

jquery.prototype.init = function() {}; // 原型初始化方法
jquery.prototype.css = function(){}; // 原型css方法

var JQ = new jquery(); //創建實例
JQ.init(); //調用初始化方法
JQ.css();  //調用css方法

這樣,我們就需要通過new來創建新的對象,如果我們將new操作寫在構造函數就可以達到類似於文檔中61到63行的作用。那麼返回的對象到底是什麼?

return new jQuery.fn.init( selector, context, rootjQuery );

這裏返回的是一個初始化構造的對象,那麼這個對象是怎麼與jQuery對象聯繫上的呢?在文檔的283行能找到關於jQuery.fn.init()設置:

這裏寫圖片描述

而關於jQuery.fn的設置在96行能找到:

這裏寫圖片描述

上面兩行指令可以等效爲如下:

jquery.prototype.init.prototype = jquery.prototype;

將一個對象的原型賦值給另外一個對象的原型,也就是原型的引用。這樣的話,改變jquery的原型也就等於改變了jquery.prototype.init的原型。也就是說在jQuery原型下定義方法,都可以在new出來的jQuery.fn.init()下使用。

二、jQuery對象的屬性和方法

96~280行給jQuery對象增加一些原型屬性和方法,其屬性和方法簡化如下:

jQuery.fn = jQuery.prototype = {

    jquery: 版本號;

    constructor: 指定構造函數;

    init(): 初始化以及管理參數,也就是選擇器的處理;

    selector:  存儲選擇字符串;

    length: this對象的長度;

    toArray(): 轉數組;

    get(): 轉原生集合; 

    pushStack():  JQ對象的入棧;

    each(): 遍歷集合;

    ready(): DOM加載的接口;

    slice(): 集合的截取;

    first(): 集合的第一項;

    last(): 集合的最後一項;

    eq(): 集合的指定項;

    map(): 返回新的集合;

    end():  返回集合前一個狀態;

    push(): 內部使用; 

    sort():  內部使用;

    splice():  內部使用;
};

下面對jQuery對象中的部分屬性和方法詳細介紹一下:

1、constructor: 指定構造函數

構造函數屬於對象的一個屬性,對象創建時就自帶一個constructor屬性,該屬性指向對象對應的構造函數。先看兩種情況下constructor的值:
第一種情況:

function aaa(){}
var Oa = new aaa();
console.log(Oa.constructor);  // function aaa(){}

第二種情況,往構造函數上添加原型屬性和方法:

function aaa(){}
aaa.prototype = {
    name : "sean",
    age : 21
};
var Oa = new aaa();
console.log(Oa.constructor); // function Object() { [native code] }

我們發現第二種情況下,我們通過這種方式給構造函數添加原型屬性和方法,改變了其原型屬性,因此對象的constructor指向也改變了。這時就需要修正其constructor屬性。

function aaa(){}
aaa.prototype = {
    name : "sean",
    age : 21
};
aaa.prototype.constructor = aaa;
var Oa = new aaa();
console.log(Oa.constructor); // function aaa(){}

修正過後的對象的constructor仍然指向其對象的構造函數。上面的jQuery.prototype中的constructor也是起相同的作用。

2、toArray(): 轉數組

該方法就是將獲取的json對象轉成數組。先來看一下其用法:

<div>1</div>
<div>2</div>
<div>3</div>
$(function(){
        console.log($("div")); //Object[div, div, div]
        console.log($("div").toArray());  // [div, div, div]
    });

第一個打印出來的是一個對象,通過toArray()方法之後轉成了數組。接下來介紹一下詳細的原理。

文檔中對jQuery對象toArray()方法的定義如下:

這裏寫圖片描述

只是用了一個JavaScript中數組的一個原型方法slice(),通過call()函數改變其上下文運行環境,this指定了運行環境爲當前對象。

再回顧一下slice()方法:slice()用於截取數組中的一部分,在只有一個參數的情況下, slice()方法返回從該參數指定位置開始到當前數組末尾的所有項。如果有兩個參數,該方法返回起始和結束位置之間的項——但不包括結束位置的項。當沒有傳入參數時,起始位置和結束位置將會自動變爲start = 0; end = this.length;。

再仔細看一眼$("div")獲得的類數組對象:

這裏寫圖片描述

其中存在一個length屬性,正好爲3,因此截取前面三個元素,然後組成數組。

3、pushStack(): JQ對象的入棧;

源碼(220~231行):

pushStack: function( elems ) {

    // Build a new jQuery matched element set
    var ret = jQuery.merge( this.constructor(), elems );

    // Add the old object onto the stack (as a reference)
    ret.prevObject = this;
    ret.context = this.context;

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

pushStack()方法在jQuery外部用到的比較少,但是在jQuery內部確實經常用到,結合文檔中的註釋我們來逐行分析一下:

var ret = jQuery.merge( this.constructor(), elems );

jQuery的merge()方法表示將兩個數組或類數組對象合併,this.constructor指向構造函數jQuery(),這個行的作用就是根據傳入的jQuery對象創建一個新的jQuery對象。

ret.prevObject = this;
ret.context = this.context;

爲新創建的對象設置prevObject屬性,這個屬性會存儲pushStack之前的jQuery對象,所以如果你對jQuery對象進行了多次查找操作,是可以順着prevObject返回的。保持對前一個對象的引用。context是記錄上下文,如果不存在多個window,可以暫時不理會。最後返回ret。、

舉例說明一下pushStack()的使用:

<div id="div1">1</div>
<div id="div2">2</div>
<div id="div3">3</div>
$("#div1").pushStack($("#div2")).css("background","red");

上述程序最終是把div2的背景顏色變成紅色。如果我們又要在此基礎上改變div1的背景色呢?我們知道先從棧裏彈出來的是div2,根據前面的分析,div2通過prevObject屬性保持了對div1的引用,因此如果我們需要改變div1的背景色,下面可以實現:

$("#div1").pushStack($("#div2")).prevObject.css("background","red");

當然這種方法是隻是用於說明參數,實際應用中,我們是不會寫成這樣。除了prevObject屬性,還有一個方法,也能達到這個效果,就是end()方法,該方法一般與pushStack()方法配套使用。

4、end(): 返回集合前一個狀態

源碼(271~273行):

end: function() {
    return this.prevObject || this.constructor(null);
},

源碼只有一行,就是返回prevObject 屬性引用的對象,如果沒有,則返回空對象。下面的寫法也能達到上面改變div1背景色的效果:

$("#div1").pushStack($("#div2")).end().css("background","red");

5、slice(): 集合的截取

源碼(247~249行):

slice: function() {
        return this.pushStack( core_slice.apply( this, arguments ) );
    },

我們發現slice()方法就用到了pushStack()方法,主要就是利用JavaScript的原生slice()方法來對類數組對象進行截取,然後在進行入棧操作。我們以例子來說明:還是上面的三個div,我們進行以下操作:

$("div").slice(1,3).css("background","red");

這樣就是將第二個和第三個div的背景色變成紅色。具體過程是:首先$("div")獲取到的是三個div,slice(1,3)操作就是截取第二個和第三個div,然後對其進行入棧操作,所以這時候,在棧最外層的就是截取的第二個和第三個div,css()也是對這兩個div起作用。

既然是pushStack()操作,那麼當然可以配套end()使用,後面加上end(),就是對$("div")獲取到的是三個div進行操作了,下面語句是改變三個div的顏色:

$("div").slice(1,3).css("background","red").end().css("color","yellow");

init(): 初始化以及管理參數,也就是選擇器的處理,是jQuery中比較重要的一部分,後續會專門進行分析。

下一章會對get()和eq()進行分析和比較。

發佈了55 篇原創文章 · 獲贊 311 · 訪問量 52萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章