一步步解析jQuery源碼

首先,咱們先去官網把JQ的js相關文件download到本地,看着源碼,仿照寫法,一步步實現而且理解jq的原理。javascript

接着建立一個屬於本身的js文件(取名爲jquerMey-1.0.1js)。html

這裏先說一下解析源碼的幾個步驟:java

  1. 學會分析組成及架構 => (JQ經過選擇器(字符串)來檢索全部匹配的DOM,而且進行批量操做,同時可以幫咱們解決瀏覽器的兼容問題。)node

  2. 學會看英文註釋(不懂多用騰訊翻譯君[手動滑稽])jquery

  3. 先減後刪webpack

  4. 閱讀思考做者的語義git

  5. 嘗試補全 好的,開搞吧!github

首先創立一個html文件,如圖:web

<!DOCTYPE html>
 <html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>jQuery源碼解析</title>
  </head>
  <body>
  <div class="box">這是一個div</div>
  <span class="box">這是一個span</span>
  <script src="jquery-3.3.1.js"></script>
  <!--<script src="sizzle.js"></script>-->
  <!--<script src="jquerMey-1.0.1.js"></script>-->
  <script type="text/javascript">
    var $eles = $('.box');
    var $eles = jQuery('.box');
    console.log($eles)
    $eles.addClass('myFirst')
  </script>
</body>
</html>
複製代碼

能夠看到,這邊jQuery.fn.init 輸出的是一個數組,還有一系列方法。咱們一步步來。

這邊先把JQ源碼的全部東西都先刪一下,能夠看到,定義一個匿名函數,建立 閉包算法

// 定義一個匿名函數,立刻調用它,包起來調用的時候能夠建立閉包
    (function(global,factory) {
        //內存中動態開闢了一塊空間來執行這個裏面的代碼,對外是封閉的,能夠訪問外面的變量
    }(typeof window !== "undefined" ? window : this, function (window, noGlobal) {
         /*這裏的三元判斷,除了BOM瀏覽器的運行環境還能運行在什麼環境中? =>node環境 (node運行在V8引擎中,主要用來作中間件) 
         中間件不少,架構與部署方面的中間件:webpack,grunt,gulp;功能方面的中間件:node.js(頁面靜態化) */
    }));
複製代碼

好,接着分析

// 定義一個匿名函數,立刻調用它,包起來調用的時候能夠建立閉包
    (function (global, factory) {
        //內存中動態開闢了一塊空間來執行這個裏面的代碼,對外是封閉的,能夠訪問外面的變量
        /*那麼除了BOM瀏覽器的運行環境還能運行在什麼環境中? =>
     node環境 (node運行在V8引擎中,主要用來作中間件) 中間件不少,
     架構與部署方面的中間件:webpack,grunt,gulp;功能方面的中間件:node.js(頁面靜態化) */
        if (typeof module === "object" && typeof module.exports === "object") {
              // For CommonJS and CommonJS-like environments where a proper `window`
            module.exports = global.document ?
                factory(global, true) :
                function (w) {
                    if (!w.document) {
                        throw new Error("jQuery requires a window with a document");
                    }
                    return factory(w);
                };
        }
        else {
            factory(global);
        }
    }(typeof window !== "undefined" ? window : this, function (window, noGlobal) {
    }));
複製代碼

寫到這裏,那麼這裏註釋說的CommonJS是什麼呢?這就涉及到了上面說的node了。

CommonJS是nodejs也就是服務器端普遍使用的模塊化機制。 該規範的主要內容是,模塊必須經過module.exports 導出對外的變量或接口,經過 require() 來導入其餘模塊的輸出到當前模塊做用域中。

能夠看到,這裏並無給factory()傳入第二個參數,默認爲false,則會執行下面if的代碼(即爲BOM環境)。在if語句中,能夠看到jQuery必定是核心代碼,那麼jQuery究竟是什麼呢?繼續看。

這裏的jQuery本質就是一個函數,jQuery有一個fn對象,而且fn有一個init函數。這裏的makeArrray本質是返回一個數組。

往下看,能夠看到這裏jQuery的fn對象其實就是jQuery的原型對象;接着咱們找到init方法。

jQuery.fn = jQuery.prototype = {
        init : function (selector, context) {
            return jQuery.makeArray( selector, context );
        }
    };
    jQuery.makeArray = function(selector, context){
        var $eles = new Sizzle(selector, context);
        return $eles;
    }
複製代碼

分析完jQuery.fn,咱們看看makeArray。Sizzle.js文件裏面有不少算法方面的代碼,咱們先跳過,繼續分析代碼。此時,咱們用Chrome打開html代碼,能夠看到,輸出如圖:(此時尚未寫addClass函數因此報錯了)

jQuery.fn = jQuery.prototype = {
        init : function (selector, context) {
            return jQuery.makeArray( selector, context );
        },
        each: function (func) {
            
        },
        addClass : function (className) {
            
        },
        removeClass: function (className) {

        }
    };
    jQuery.makeArray = function(selector, context){
        var $eles = new Sizzle(selector, context);
        $eles.prevObject = arguments.callee;
        $eles.__proto__ = jQuery.fn
        return $eles;
    }
複製代碼

繼續補全,這樣jQuery的 總體架構 就ok了,以後就是往裏面添加東西。

(好比往裏面添加addClass,removeClass,each方法)

jQuery.fn = jQuery.prototype = {
        init : function (selector, context) {
            return jQuery.makeArray( selector, context );
        },
        each: function (func) {
            for (var i=0;i<this.length;i++) {
                func.call(this,i,this[i]);
            }
            return this;
        },
        addClass : function (className) {
            return this.each(function (index, element) {
               element.className += " " + className
            })
        },
        removeClass: function (className) {
            return this.each(function (index, element) {
                element.className = ""
            })
        }
    };
 
複製代碼

咱們能夠看到此時控制檯裏面已經有了咱們添加的方法,讓咱們來實驗一下。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>jQuery源碼解析</title>
</head>
<style>
    .myEleFirst {
        display: block;
        width: 100px;
        height: 100px;
        margin: 10px auto;
        background: red;
    }
    button {
        display: block;
        width: 20px;
        height: 20px;
        margin: auto;
   }
  </style>
  <body>
   <div class="myEle">這是一個div</div>
   <span class="myEle">這是一個span</span>
   <button onclick="removeClass()"></button>
   <!--<script src="jquery-3.3.1.js"></script>-->
  <script type="text/javascript" src="sizzle.min.js"></script>
  <script type="text/javascript" src="jquerMey-1.0.1.js"></script>
  <script type="text/javascript">
    var $eles = $(".myEle");
    var $eles = jQuery(".myEle");
    console.log($eles);
    $eles.addClass('myEleFirst');
    function removeClass() {
        $eles.removeClass("myEleFirst")
    }
  </script>
  </body>
</html>
複製代碼

結果如圖:

附上所有代碼:

/*!
 * jqueMey JavaScript Library v1.0.1
 *
 * Includes Sizzle.js
 * https://sizzlejs.com/
 *
 * Copyright JS Foundation and other contributors
 * Released under the MIT license
 * Email: huangmiantong@126.com || v_mtonhuang@tencent.com
 *
 * Date: 2018-12-11T22:04Z
 */

// 定義一個匿名函數,立刻調用它,包起來調用的時候能夠建立閉包
(function (global, factory) {
    //在BOM瀏覽器的運行環境
    /*那麼除了BOM瀏覽器的運行環境還能運行在什麼環境中? =>
     node環境 (node運行在V8引擎中,主要用來作中間件) 中間件不少,
     架構與部署方面的中間件:webpack,grunt,gulp;功能方面的中間件:node.js(頁面靜態化) */
    //內存中動態開闢了一塊空間來執行這個裏面的代碼
    if (typeof module === "object" && typeof module.exports === "object") {
        module.exports = global.document ?
            factory(global, true) :
            function (w) {
                if (!w.document) {
                    throw new Error("jQuery requires a window with a document");
                }
                return factory(w);
            };
    } else {
        factory(global);
    }
}(typeof window !== "undefined" ? window : this, function (window, noGlobal) {
    var
        version = "1.0.1",

        // 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 );
        };
    jQuery.fn = jQuery.prototype = {
        init : function (selector, context) {
            return jQuery.makeArray( selector, context );
        },
        each: function (func) {
            for (var i=0;i<this.length;i++) {
                func.call(this,i,this[i]);
            }
            return this;
        },
        addClass : function (className) {
            return this.each(function (index, element) {
               element.className += " " + className
            })
        },
        removeClass: function (className) {
            return this.each(function (index, element) {
                element.className = ""
            })
        }
    };
    jQuery.makeArray = function(selector, context){
        var $eles = new Sizzle(selector, context);
        $eles.prevObject = arguments.callee;
        $eles.__proto__ = jQuery.fn
        return $eles;
    }
    // Expose jQuery and $ identifiers, even in AMD
    // (#7102#comment:10, https://github.com/jquery/jquery/pull/557)
    // and CommonJS for browser emulators (#13566)
    if (!noGlobal) {
        //BOM必定有window對象
        // jQuery必定是核心對象
        window.jQuery = window.$ = jQuery;
    }
    return jQuery;
}));
 

複製代碼

未完待續...

相關文章
相關標籤/搜索