Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

一步步分析JQ源码 #18

Open
mtonhuang opened this issue Dec 9, 2020 · 0 comments
Open

一步步分析JQ源码 #18

mtonhuang opened this issue Dec 9, 2020 · 0 comments

Comments

@mtonhuang
Copy link
Owner

首先,我们先去官网把JQ的js相关文件download到本地,随意浏览一下源码,尝试理解jq的实现原理。

接着我们创建一个属于自己的js文件(取名为jquerMey-1.0.1js)。

这里先说一下解析源码的几个步骤:

  1. 学会分析组成及架构 =>
    (JQ通过选择器(字符串)来检索所有匹配的DOM,并且进行批量操作,同时能够帮我们解决浏览器的兼容问题。)

  2. 学会看英文注释(不懂多用腾讯翻译君[手动滑稽])

  3. 先减后删

  4. 阅读思考作者的语义

  5. 尝试补全
    好的,开搞吧!

一、创立一个html文件

<!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

CommonJS是nodejs也就是服务器端广泛使用的模块化机制。 该规范的主要内容是,模块必须通过module.exports 导出对外的变量或接口,通过 require() 来导入其他模块的输出到当前模块作用域中。

四、JQuery的本质

可以看到,这里并没有给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
 *
 * 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;
}));
 

未完待续...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant