由於 core 本身帶著非常多的碼,筆者想針對幾個比較重要的點來解讀,所以我們先把它簡化成這樣:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var jQuery = (function() { | |
// Define a local copy of jQuery | |
var jQuery = function( selector, context ) { | |
// The jQuery object is actually just the init constructor 'enhanced' | |
return new jQuery.fn.init( selector, context ); | |
}, | |
// A central reference to the root jQuery(document) | |
rootjQuery; | |
jQuery.fn = jQuery.prototype = { | |
constructor: jQuery, | |
init: function( selector, context, rootjQuery ) { | |
this.context = this[0] = document.body; | |
this.length = 1; | |
return this; | |
}, | |
// The default length of a jQuery object is 0, and make it act like [] | |
length: 0, | |
splice: [].splice | |
}; | |
// Give the init function the jQuery prototype for later instantiation | |
jQuery.fn.init.prototype = jQuery.fn; | |
rootjQuery = jQuery(document); | |
return jQuery; | |
})(); |
從上面的 code 來看,我們一般在使用 jQuery 時,最常用的是 $("selector"),而 $ 其實就等於 window.jQuery (core 這邊其實有一段 code 在處理 window.jQuery / window.$ 可能會有 naming conflict 的問題,我們這次先跳過)。 所以 $ 實際上會是執行
new jQuery.fn.init( selector, context );
這邊呢,jQuery.fn.init 是 jQuery.prototype 中的一個 function,而由於這個敘述:
jQuery.fn.init.prototype = jQuery.fn; // jQuery.fn = jQuery.prototype
使得 jQuery.fn.init 也成為 jQuery 的 constructor。這樣一來的話,基於 prototype 繼承的特性,就可以透過在 jQuery.fn 上動態新增 function 來達到擴充的功能。也就是說由於 JS 的物件都會有一個 reference 指向 prototype object,因此在後面新增到 prototype 的 function 也會讓已經生成的 object 具有該新 function。
而以 jQuery 為基礎來開發的人一定很清楚 jQuery 的可串接性 (chaining) 的寫法。比如說這一段 code:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// No chaining | |
$("#menu").fadeIn('fast'); | |
$("#menu").addClass(".active"); | |
$("#menu").css('marginRight', '10px'); |
其實可以寫成:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// vs. with chaining | |
$("#menu").fadeIn('fast').addClass("active").css('marginRight', '10px'); | |
// or | |
$("#menu").fadeIn('fast') | |
.addClass("active") | |
.css('marginRight', '10px'); |
之所以可以這樣寫是因為幾乎每一個 jQuery function 都會 return this; // i.e. jQuery object。當然會有例外,比如 .val() 就不行。 也就是說當要擴充 jQuery.fn 的時候也必須要把 chainability 考慮進去。
再來有一點,我覺得很少人討論,我之前在用 jQuery 的時候,每次下 selector,jQuery 都會回傳一個 array,在 inspector 裡面都會看到 jQuery.fn.jQuery.init[n] ,每次我都覺得很奇怪,但是稍微看一下 code 又看不出為什麼。後來我很認真地研究終於發現原來只要一個 object 裡面有 length 這個屬性,同時又有 splice 這個 function 那行為就會看起來很像 array 但是並不是真的 array instance。
很容易驗證,在 console 裡面這樣打就可以看得出來了:
好的, jQuery 原始碼解讀第二趴就先到這裡了。 待續...
jQuery 系列文章:
沒有留言:
張貼留言