zoukankan      html  css  js  c++  java
  • 【jQuery源码】整体架构

      jQuery源码可以精简为以下内容:

      方框上面的代码根据Jq注释我们可以知道是对AMD规范的支持。

      jQuery整体上被包裹在一个匿名函数中,这个匿名函数再作为另一个匿名函数的参数被传入,形参factory。

    一、自调用函数

      整个部分被自调用函数包裹,通过定义一个匿名函数,创建了一个“私有”的命名空间,该命名空间的变量和方法,不会破坏全局的命名空间。

      自调用函数中的第一个参数相当于window,通过传入window变量,使得window由全局变量变为局部变量,当在jQuery代码块中访问window时,不需要将作用域链回退到顶层作用域,这样可以更快的访问window;这还不是关键所在,更重要的是,将window作为参数传入,可以在压缩代码时进行优化。

      第二个参数为包裹jQuery的匿名函数,在匿名参数中被调用——factory()。

    二、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
    复制代码

    三、链式调用

      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()

      优点:节省代码量,提高代码的效率,代码看起来更优雅

    四、插件接口

      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 };
  • 相关阅读:
    判断用户是否登录
    django 请求中的认证
    django 验证密码
    CXF+Spring搭建webservice服务
    CXF+Spring搭建webservice服务
    CXF+Spring搭建webservice服务
    关于本地用svn up的时候报cannot update svn folder: "unversioned directory of the same name already exists...
    关于本地用svn up的时候报cannot update svn folder: "unversioned directory of the same name already exists...
    关于本地用svn up的时候报cannot update svn folder: "unversioned directory of the same name already exists...
    关于本地用svn up的时候报cannot update svn folder: "unversioned directory of the same name already exists...
  • 原文地址:https://www.cnblogs.com/shytong/p/5311108.html
Copyright © 2011-2022 走看看