【jQuery源碼】總體架構

jQuery源碼能夠精簡爲如下內容:javascript

  方框上面的代碼根據Jq註釋咱們能夠知道是對AMD規範的支持。css

  jQuery總體上被包裹在一個匿名函數中,這個匿名函數再做爲另外一個匿名函數的參數被傳入,形參factory。html

 

1、自調用函數java

  整個部分被自調用函數包裹,經過定義一個匿名函數,建立了一個「私有」的命名空間,該命名空間的變量和方法,不會破壞全局的命名空間。jquery

  自調用函數中的第一個參數至關於window,經過傳入window變量,使得window由全局變量變爲局部變量,當在jQuery代碼塊中訪問window時,不須要將做用域鏈回退到頂層做用域,這樣能夠更快的訪問window;這還不是關鍵所在,更重要的是,將window做爲參數傳入,能夠在壓縮代碼時進行優化。編程

  第二個參數爲包裹jQuery的匿名函數,在匿名參數中被調用——factory()。api

 

2、jQuery的無new構建數組

  jQuery框架的核心就是從HTML文檔中匹配元素並對其執行操做、瀏覽器

  例如:框架

$().find().css()
$().hide().html('....').hide().

  從上面的寫法上至少能夠發現2個問題

  1. jQuery對象的構建方式

  2 .jQuery方法的調用方式

 

分析一:jQuery的無new構建

  JavaScript是函數式語言,函數能夠實現類,類就是面向對象編程中最基本的概念

複製代碼
複製代碼
var aQuery = function(selector, context) {
        //構造函數
}
aQuery.prototype = {
    //原型
    name:function(){},
    age:function(){}
}

var a = new aQuery();

a.name();
複製代碼
複製代碼

  這是常規的使用方法,顯而易見jQuery不是這樣玩的

  jQuery沒有使用new運行符將jQuery顯示的實例化,仍是直接調用其函數

  按照jQuery的抒寫方式

$().ready() 
$().noConflict()

  要實現這樣,那麼jQuery就要當作一個類,那麼$()應該是返回類的實例纔對

  因此把代碼改一下:

複製代碼
複製代碼
var aQuery = function(selector, context) {
       return new aQuery();
}
aQuery.prototype = {
    name:function(){},
    age:function(){}
}
複製代碼
複製代碼

  經過new aQuery(),雖然返回的是一個實例,可是也能看出很明顯的問題,死循環了!

 


那麼如何返回一個正確的實例?

  在javascript中實例this只跟原型有關係

  那麼能夠把jQuery類看成一個工廠方法來建立實例,把這個方法放到jQuery.prototye原型中

複製代碼
複製代碼
var aQuery = function(selector, context) {
       return  aQuery.prototype.init();
}
aQuery.prototype = {
    init:function(){
        return this;
    }
    name:function(){},
    age:function(){}
}
複製代碼
複製代碼

  當執行aQuery() 返回的實例:

image

  很明顯aQuery()返回的是aQuery類的實例,那麼在init中的this其實也是指向的aQuery類的實例

  問題來了init的this指向的是aQuery類,若是把init函數也看成一個構造器,那麼內部的this要如何處理?

複製代碼
複製代碼
var aQuery = function(selector, context) {
       return  aQuery.prototype.init();
}
aQuery.prototype = {
    init: function() {
        this.age = 18
        return this;
    },
    name: function() {},
    age: 20
}

aQuery().age  //18
複製代碼
複製代碼

  這樣的狀況下就出錯了,由於this只是指向aQuery類的,因此須要設計出獨立的做用域才行

 


jQuery框架分隔做用域的處理

jQuery = function( selector, context ) {
        // The jQuery object is actually just the init constructor 'enhanced'
        return new jQuery.fn.init( selector, context, rootjQuery );
    },

  很明顯經過實例init函數,每次都構建新的init實例對象,來分隔this,避免交互混淆

  那麼既然都不是同一個對象那麼確定又出現一個新的問題

例如:

複製代碼
複製代碼
var aQuery = function(selector, context) {
       return  new aQuery.prototype.init();
}
aQuery.prototype = {
    init: function() {
        this.age = 18
        return this;
    },
    name: function() {},
    age: 20
}

//Uncaught TypeError: Object [object Object] has no method 'name' 
console.log(aQuery().name())
複製代碼
複製代碼

  拋出錯誤,沒法找到這個方法,因此很明顯new的init跟jquery類的this分離了

 


怎麼訪問jQuery類原型上的屬性與方法?

     作到既能隔離做用域還能使用jQuery原型對象的做用域呢,還能在返回實例中訪問jQuery的原型對象?

實現的關鍵點

// Give the init function the jQuery prototype for later instantiation
jQuery.fn.init.prototype = jQuery.fn;

  經過原型傳遞解決問題,把jQuery的原型傳遞給jQuery.prototype.init.prototype

  換句話說jQuery的原型對象覆蓋了init構造器的原型對象

  由於是引用傳遞因此不須要擔憂這個循環引用的性能問題

複製代碼
複製代碼
var aQuery = function(selector, context) {
       return  new aQuery.prototype.init();
}
aQuery.prototype = {
    init: function() {
        return this;
    },
    name: function() {
        return this.age
    },
    age: 20
}

aQuery.prototype.init.prototype = aQuery.prototype;

console.log(aQuery().name()) //20
複製代碼
複製代碼

 

3、鏈式調用

  DOM鏈式調用的處理:

  1.節約JS代碼.

  2.所返回的都是同一個對象,能夠提升代碼的效率

 

  經過簡單擴展原型方法並經過return this的形式來實現跨瀏覽器的鏈式調用。

  利用JS下的簡單工廠模式,來將全部對於同一個DOM對象的操做指定同一個實例。

  這個原理就超簡單了

複製代碼
aQuery().init().name()

分解
a = aQuery();
a.init()
a.name()
複製代碼

  把代碼分解一下,很明顯實現鏈式的基本條件就是實例this的存在,而且是同一個

複製代碼
複製代碼
aQuery.prototype = {
    init: function() {
        return this;
    },
    name: function() {
        return this
    }
}
複製代碼
複製代碼

  因此咱們在須要鏈式的方法訪問this就能夠了,由於返回當前實例的this,從而又能夠訪問本身的原型了

aQuery.init().name()

  優勢:節省代碼量,提升代碼的效率,代碼看起來更優雅

 

4、插件接口

  jQuery支持本身擴展屬性,這個對外提供了一個接口,jQuery.fn.extend()來對對象增長方法

從jQuery的源碼中能夠看到,jQuery.extend和jQuery.fn.extend實際上是同指向同一方法的不一樣引用

jQuery.extend = jQuery.fn.extend = function() {
jQuery.extend 對jQuery自己的屬性和方法進行了擴展,擴展工具方法下的插件

jQuery.fn.extend 對jQuery.fn的屬性和方法進行了擴展,擴展jquery對象下的插件

  經過extend()函數能夠方便快速的擴展功能,不會破壞jQuery的原型結構

  jQuery.extend = jQuery.fn.extend = function(){...}; 這個是連等,也就是2個指向同一個函數,怎麼會實現不一樣的功能呢?這就是this 力量了!

  針對fn與jQuery實際上是2個不一樣的對象,在以前有講述:

  • jQuery.extend 調用的時候,this是指向jQuery對象的(jQuery是函數,也是對象!),因此這裏擴展在jQuery上。
  • 而jQuery.fn.extend 調用的時候,this指向fn對象,jQuery.fn 和jQuery.prototype指向同一對象,擴展fn就是擴展jQuery.prototype原型對象。
  • 這裏增長的是原型方法,也就是對象方法了。因此jQuery的api中提供了以上2中擴展函數。
複製代碼
1 jQuery.extend = jQuery.fn.extend = function() {
 2     var src, copyIsArray, copy, name, options, clone,
 3         target = arguments[0] || {},
 4         i = 1,
 5         length = arguments.length,
 6         deep = false;
 7 
 8     // Handle a deep copy situation
 9     //判斷第一個參數是否爲boolean類型,也就是是否深度嵌套對象
10     if ( typeof target === "boolean" ) {
11         deep = target;
12 
13         // skip the boolean and the target
14         //跳過第一個boolean參數
15         target = arguments[ i ] || {};
16         i++;
17     }
18 
19     // Handle case when target is a string or something (possible in deep copy)
20     // 處理奇怪的狀況,好比 jQuery.extend( 'hello' , {nick: 'casper})
21     if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
22         target = {};
23     }
24 
25     // extend jQuery itself if only one argument is passed
26     if ( i === length ) {
27         target = this;
28         i--;
29     }
30 
31     for ( ; i < length; i++ ) {
32         // Only deal with non-null/undefined values
33         if ( (options = arguments[ i ]) != null ) {//opitions爲一個個參數對象
34             // Extend the base object
35             for ( name in options ) {
36                 src = target[ name ];
37                 copy = options[ name ];
38 
39                 // Prevent never-ending loop
40                 //防止自引用
41                 if ( target === copy ) {
42                     continue;
43                 }
44 
45                 // Recurse if we're merging plain objects or arrays
46                 //深拷貝
47                 if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
48                     if ( copyIsArray ) {//被拷貝屬性值是個數組
49                         copyIsArray = false;
50                         clone = src && jQuery.isArray(src) ? src : [];
51 
52                     } else {
53                         clone = src && jQuery.isPlainObject(src) ? src : {};
54                     }
55 
56                     // Never move original objects, clone them
57                     //遞歸拷貝
58                     target[ name ] = jQuery.extend( deep, clone, copy );
59 
60                 // Don't bring in undefined values
61                 } else if ( copy !== undefined ) {//淺拷貝,屬性值不是自定義
62                     target[ name ] = copy;
63                 }
64             }
65         }
66     }
67 
68     // Return the modified object
69     return target;
70 };
複製代碼
相關文章
相關標籤/搜索