jQuery 原始碼分析和使用心得 - core.js

edithfang發表於2014-09-17
建構函式 jQuery( selector, context )

說到jQuery, 大家可能最熟悉的就是 $(selector, context) , 我覺得這也是jQuery受到人們歡迎的很大一部分原因, 因為這個方法非常的強大.
    // Define a local copy of jQuery jQuery = function( selector, context ) { // The jQuery object is actually just the init constructor 'enhanced' // Need init if jQuery is called (just allow error to be thrown if not included) return new jQuery.fn.init( selector, context );
    },

這裡我們很明顯的看到一個關鍵詞就是new ,這說明每一次呼叫$()就會new一個jQuery物件出來 , 有些人可能不注意這一點就會寫出下面的程式碼, 或者會出現 $("#id") === $("#id")的這種判斷 , 這種寫法都是不可取的, 我們應該儘量的節省jQuery的獲取次數, 重複利用jQuery物件.
if($(".class").hasClass()){
    $(".class").removeClass();
}

靜態屬性和原型屬性

從core.js中可以看到兩類屬性( q君: 函式也是屬性哦! ), 一類是以jQuery.extend({})定義的靜態屬性, 一類是通過  jQuery.fn = jQuery.prototype = {} 進行定義的原型屬性. 靜態屬性是jQuery函式的屬性, 通過 $呼叫, 如$.each . 靜態屬性通常為與頁面元素無關的工具方法. 原型屬性是擴充jQuery物件的一些功能, 它持有jQuery物件自身(this) , 為處理自身物件之用 呼叫形式為 var $a = $("a"); $a.each() .( q君: jQuery裡面有兩個each ,要分清 )

具體的實現方法都比較簡單, 就不再一一敘述. jQuery.extend 是一個很常用的方法, 它可以將幾個物件合併成一個物件, 並且支援深拷貝和淺拷貝兩種方式 , 在做一些預設配置和實現的時候會經常用到.

jQuery物件建立

通過檢視 jQuery( selector, context )函式, 知道了 jQuery 呼叫了 new jQuery.fn.init( selector, context ). 在這句話上面有一條註釋: Need init if jQuery is called .  我們在 /src/core/目錄下找到init.js. 內容大致如下

init = jQuery.fn.init = function( selector, context ) { // ... // ... return this; // ... retrun this.constructor( context ).find( selector ); // ... return ( context || rootjQuery ).find( selector ); // ... return typeof rootjQuery.ready !== "undefined" ?
                rootjQuery.ready( selector ) : // Execute immediately if ready is not present selector( jQuery ); // ... return jQuery.makeArray( selector, this );    
} // Give the init function the jQuery prototype for later instantiation init.prototype = jQuery.fn;

在這個方法裡有很多種返回.

第一種 this, 返回的是init的物件;

第二種 jquery物件.find(); -> /src/traversing/findFilter.js : find -> return this.pushStack -> return jQuery.merge( this.constructor(), elems ); -> jQuery物件

第三種 selector(jQuery); 不一定返回jQuery物件

第四種 rootjQuery.ready( selector ); -> /src/core/ready.js : ready -> return this(rootjQuery) -> jQuery物件

第五種 jQuery.makeArray( selector, this ); -> var ret = results; return ret; -> jQuery物件

通過上述觀察, 發現jQuery方法只有一種情況下返回的不是jQuery物件: 傳入的selector引數為物件, 並且沒有載入 /src/core/ready.js 模組. 下面這段程式碼也會證明這種情況(程式碼會在附件中, 請及時更新).

<!DOCTYPE html> <html> <head> <meta charset="utf-8">  <title>core</title>  </head> <body> <script src="require.js" data-main="main" type="text/javascript" charset="utf-8"></script> <script type="text/javascript" charset="utf-8"> require(['../src/core/init'], function(init) { var ret = new init(function() { return {
                        name: "not jquery" };
                });
                console.log(ret);
            }) </script> </body> </html>

再來看第一種情況, return this返回的是init物件, 並不是jQuery物件, 但是我們在下面看到 init.prototype = jQuery.fn; 所以通過init建立的物件也就是jQuery物件.

jQuery實現偽陣列

我們在用jQuery的過程中很多情況下會把jQuery當做陣列去用. 但是我們從上面建立過程來看, jQuery貌似跟陣列並沒有什麼關係. 那麼我們來分析一下他的原型看看為什麼jQuery可以像陣列一樣使用.

在這之前我先介紹一下"鴨子型別(duck typing)" ( q君: 鴨子? 好吃嗎? ), 什麼是鴨子型別? “當看到一隻鳥走起來像鴨子、游泳起來像鴨子、叫起來也像鴨子,那麼這隻鳥就可以被稱為鴨子。” 這是一個非常恰當的描述, 在動態語言中我們不需要管一個物件到底是什麼型別, 只要他能執行某個方法, 那麼我就可以認為他就是某個型別的物件.

Javascript就是這樣的一種語言. 要實現陣列不一定要繼承或者使用Array物件. 只要實現了Array物件裡面的方法, 就可以認為他是陣列. 這裡我也發現一個很有趣的事情. 用chrome瀏覽器進行除錯的時候, 我們用console.log輸出普通物件和陣列的時候輸出的格式不一樣. 輸出物件類似於 " f {length: 0} " , 輸出陣列類似於 " [1] ". 我們通過下面幾行程式碼做個試驗
var f = function(){this.length = 1; this[0] = "a" }
console.log(new f()); // f {0: "a", length: 1} var f = function(){this.length = 1;this.splice = function(){};this[0] = "a" }
console.log(new f()); // ["a"]

哈, 多麼神奇! chrome把他當成陣列了! 如果一個物件中length為數字, splice為方法, 這個物件就會被chrome認為是陣列. 大家感興趣的話可以試一下其他的瀏覽器.

嗯, 我們回過頭看jQuery的prototype, jQuery的prototype中包含了陣列中常用的 length屬性, push , sort , splice , slice等方法.

所有jQuery就像一個陣列一樣使用.

使用建議

  •   1. 儘量減少重複使用 $()方法獲取相同的物件
  •   2. $() 大部分會返回jQuery物件, 但不排除在極端情況
  •   3. jQuery可以當做陣列使用, 但不完全是陣列, pop, shift 等方法不適用於jQuery物件.

附件
  http://yunpan.cn/Q7nKEsZ9cqFby  提取碼 3a4a
評論(2)

相關文章