jQuery 源碼剖析(一) - 核心功能函數

jQuery 源碼解析代碼及更多學習乾貨: 猛戳GitHub前端

建議下載源碼而後據文章思路學習,最好本身邊思考邊多敲幾遍。node

開篇題外話:爲何要寫這篇文章?

提到jQuery,相信不少前端工程師都知道,這個已經火了十來年的框架,爲前端開發提供便利性的同時也解決了各類各樣的瀏覽器兼容性問題,一個框架爲何這麼火🔥其中的緣由不言而喻,但可否以一種第三人稱的方式,站在做者的角度來來思考設計,這估計是不少人不肯意去作的事,那麼今天開始,我想以第三人稱的方式來剖析源碼,自問自答的方式,讀懂做者的意圖,體會大牛的編程思想,學以至用,提高編碼的思想和格局。jquery

一:剖析源碼前準備

  • 1.首先官網下載源碼jQuery官網
  • 2.選擇jQuery版本並下載到本地,並在本地給本身新建件myjQuery-1.0.0.js(這個文件是用來仿寫jQuery).
  • 3.建立入口文件並引入這官方jQuery和本身建立的myjQuery-1.0.0.js文件.
  • 4.開始剖析源碼.

二:剖析源碼採用的方法技巧

  • 1.裁剪
  • 2.註釋翻譯
  • 3.一目十行找關鍵類名
  • 4.註釋掉不相關干擾代碼
  • 5.仿寫

開始剖析

本篇經過三個方向來剖析jQuery的精髓.git

1.jQuery無new構建實例github

(1)爲何jQuery對象能夠經過$符號直接能夠訪問呢? 咱們先來看下下面這張 jQuery共享原型設計圖:編程

jQuery共享原型設計圖

經過上圖分解,能夠很清晰的分析出最佳方案: 建立一個jQuery對象,返回jQuery原型對象的init方法,而後共享原型,將jQuery掛載到windows上起別名$,實現經過$來訪問jQuery的構造函數.同理經過$.fn來替代jQuery.prototype。windows

// 閉包 當即執行函數
;(function(root){
    var jQuery = function() {
        return new jQuery.prototype.init();
    }
   jQuert.fn = jQuery.prototype = {
        
    }

    // 共享原型對象
    jQuert.fn.init.prototype = jQuert.fn;
    
    root.$ = root.jQuery = jQuery;

})(this);
複製代碼

2.共享原型設計設計模式

上面的代碼已經很明顯的體現出共享原型設計的思想,將jQuery原型對象共享,而後經過擴展實例方法屬性以及添加靜態屬性及靜態方法的形式充分實現jQuery的靈活擴展性。數組

3.extend源碼解析 在使用jQuery源碼中咱們有時候爲了給源碼擴展一個新的方法,通常會採用如下幾種方式:瀏覽器

// 任意對象擴展
var obj = $.extend({},{name:"james"});

// 自己擴展
$.extend({
    work:function(){
    }
});

// 實例對象擴展
$.fn.extend({
    sex:"男"
});
$().sex;  // 男
複製代碼

經過以上代碼咱們來反推,jQuery源碼中extend是如何實現的。

jQuery.fn.extend = jQuery.extend = function () {
        var target = arguments[0] || {};
        var length = arguments.length;
        // 從第1個參數開始解析,由於第0個是咱們targer,用來接收解析過的數據的
        var i = 1;
        var option,name;
        if(typeof target !== "object") {
            target = {};
        }

        // 淺拷貝
        for (;i<length;i++){
            if((option = arguments[i]) != null) {
                for(name in option) {
                    target[name] = option[name];
                }
            }
        }
        return target;
    }
複製代碼

測試代碼:

var ret = {name:'james',list:{age:26,sex:'女'}};
 var obj = $.extend({},ret); 
 console.log(obj);
複製代碼

如下是輸出結果:

此時咱們成功的擴展了一個爲任意對象擴展的extender方法。

問題來了 既然是任意對象,那麼咱們是否能夠經過extender方法來擴展多個屬性呢? 測試代碼:

var ret = {name:'james',list:{age:26,sex:'女'}};
var res = {list:{sex:'男'}} 
var obj = $.extend({},ret,res); 
複製代碼

如下是輸出結果:

咦???不對吧,明明我是寫了兩個對象,怎第一個對象的list屬性被覆蓋掉了??逗我呢.....

經過仔細閱讀源碼,終於得出告終論,extender方法擴展了淺拷貝和深拷貝,因而從新寫了擴展方法,經過傳入一個boolean參數來決定是否須要深拷貝。

jQuery.extend = jQuery.fn.extend = function () {
       // 聲明變量
       var options,name,copy,src,copyIsArray,clone,
       target = arguments[0] || {},
       length = arguments.length,
       // 從第1個參數開始解析,由於第0個是咱們targer,用來接收解析過的數據的
       i = 1,
       // 是不是深拷貝,外界傳過來的第一個參數
       deep = false;
   
       // 處理深層複製狀況 
       if(typeof target === "boolean") {
           // extender(deep,{},obj1,obj2) 
           deep = target;
           target = arguments[i] || {};
           i ++;
       }
       // 判斷 targer不是對象也不是方法
       if(typeof target !== "object" && !isFunction(target)) {
           target = {};
       } 
   
       // 若是隻傳遞一個參數,則擴展jQuery自己
       if (length === i) {
           target = this;
           // 此時把i變爲0
           i--;
       }
   
       for ( ; i < length ; i++){
           // 僅處理非null /未定義的值
           if((options = arguments[i]) != null) {
   
               // 僅處理非null /未定義的值
               for(name in options) {
                   copy = options[name];
                   src = target[name];
   
                   // 防止Object.prototype污染
                   // 防止死循環循環 
                   if (name === "__proto__" || target == copy) {
                       continue;
                   }
   
                   //若是咱們要合併普通對象或數組,請遞歸
                   // 此時的copy必須是數組或者是對象
                   if ( deep &&  (jQuery.isPlainObject(copy) ||
   				(copyIsArray = jQuery.isArray(copy)))) {
   
                       // 確保源值的正確類型  源值只能是數組或者對象
                       if ( copyIsArray ) {
                           copyIsArray = false;
                           clone = src && jQuery.isArray(src)?src:[];
                       } else {
                           clone = src && jQuery.isPlainObject(src)?src:{};
                       } 
                       //永遠不要移動原始對象,克隆它們
                       target[name] = jQuery.extend(deep,clone,copy);
   
                       //不要引入未定義的值
                   } else if (copy !== undefined){
                       // 淺拷貝
                       target[name] = copy;
                   }
               }
           }
       }
       //返回修改後的對象 
       return target;
   };
複製代碼

擴展了三個屬性判斷:

// 判斷是不是方法
   var isFunction = function isFunction( obj ) {   
   return typeof obj === "function" && typeof obj.nodeType !== "number";
   };

// 擴展屬性和方法
   jQuery.extend({
       // 類型檢測 是不是對象
       isPlainObject: function(obj) {
           // "[object Object]" 第二個O必定是大寫,坑了我好幾個小時.......
           return toString.call(obj) === "[object Object]";
       },
       // 是不是數組
       isArray: function(obj) {
           return toString.call(obj) === "[object Array]";
       }
   });
複製代碼

測試代碼:

var a = {name:"james",list:{age:"26"}};
   var b = {list:{sex:"男"}}; 
   var c = $.extend(true,{},a,b); 
複製代碼

終於大功告成:輸出了我想要的結果:合併了相同鍵的對象.

總結

本篇主要分享jQuery入口函數共享原型鏈思想以及核心功能擴展函數extend的剖析.

其餘

jQuery 源碼剖析 系列目錄地址:猛戳GitHub

jQuery 源碼剖析 系列預計寫十篇左右,旨在加深對原生JavaScript 部分知識點的理解和深刻,重點講解 jQuery核心功能函數、選擇器、Callback 原理、延時對象原理、事件綁定、jQuery體系結構、委託設計模式、dom操做、動畫隊列等

若是有錯誤或者不嚴謹的地方,請務必給予指正,十分感謝。若是喜歡或者有所啓發,歡迎 star,對做者也是一種鼓勵。

關注公衆號回覆:學習 領取前端最新最全學習資料,也能夠進羣和大佬一塊兒學習交流

相關文章
相關標籤/搜索