• JQuery源码解析-JQuery.extend()方法


    extend方法是jQuery中的继承方法,先说一下extend方法的使用,在进行源码解析。

    当extend只有一个参数的时候,代表将对象扩展到jQuery的静态方法或实例方法中,如:

        $.extend({
                a: function () {
                    alert("a");
                }
            
            })
         $.fn.extend({
                a: function () {
                    alert("a");
                }
            })
            $.a();
            $().a();

    在上面的代码可以看出不管是jQuery对象还是实例,都可以用extend方法进行继承,在源码中也是调用的同一个方法,之所以可以这么做的原因是因为在源码中,内部绑定时,用到了this。

    $.extend的this就是$ 而 $.fn.extend的this是$.fn,也就是代表实例的原型上扩展。

    再看一下传入多个参数的情况,当传入多个参数时,如果第一个参数不是bool类型,默认后面的参数的属性都会被添加到一个参数对象上。

    如果第一个参数为bool类型且为true,则代表深拷贝,默认为浅拷贝,false。

         var a = {};
            var b = { tom: { age: 14 } }
            $.extend(a, b);
            a.tom.age = 25;
            console.log(a.tom.age); //25
            console.log(b.tom.age);//25

    上面的代码的问题可以看到,当继承的对象属性中有引用类型的时候,那么会造成两个两个对象同时指向一个对象,这样如果改变一个的话,另一个也随之改变,所以:

    $.extend(true,a, b);

    把第一个值给true,进行深拷贝就可以了。

    下面看一下extend方法内部的源码。

    内部的大体结构如下:

    jQuery.extend = jQuery.fn.extend = function() {
    //定义一些参数
    if(){}    //看是不是深拷贝的情况。
    if(){}    //看参数是否正确
    if(){}    //看是不是插件的情况
    for(){     //处理多个对象参数
        if(){}             //防止循环调用
        if(){}            //深拷贝
        else if(){}     //浅拷贝
    }
    }        

    第一部分定义一些参数:

    var options, name, src, copy, copyIsArray, clone,
            target = arguments[0] || {},
            i = 1,
            length = arguments.length,
            deep = false;

    然后进行判断,看是否第一个参数传入的是bool值,如果是,则将其赋值给deep,然后将target赋值为第二个参数。

    // Handle a deep copy situation
        if ( typeof target === "boolean" ) {
            deep = target;
            target = arguments[1] || {};
            // skip the boolean and the target
            i = 2;
        }

    然后进行判断,看target是否为对象或函数,如果非对象,如字符串等,则将其赋值为空对象。

    // Handle case when target is a string or something (possible in deep copy)
        if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
            target = {};
        }

    然后判断是否为扩展工具方法,如果是的话,则直接将target赋值为this

    // extend jQuery itself if only one argument is passed
        if ( length === i ) {
            target = this;
            --i;
        }

    接下来是最后一个段也是这个方法中最复杂的一块:

    for ( ; i < length; i++ ) {
            // Only deal with non-null/undefined values
            if ( (options = arguments[ i ]) != null ) {
                // Extend the base object
                for ( name in options ) {
                    src = target[ name ];
                    copy = options[ name ];
    
                    // Prevent never-ending loop
                    if ( target === copy ) {
                        continue;
                    }
    
                    // Recurse if we're merging plain objects or arrays
                    if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
                        if ( copyIsArray ) {
                            copyIsArray = false;
                            clone = src && jQuery.isArray(src) ? src : [];
    
                        } else {
                            clone = src && jQuery.isPlainObject(src) ? src : {};
                        }
    
                        // Never move original objects, clone them
                        target[ name ] = jQuery.extend( deep, clone, copy );
    
                    // Don't bring in undefined values
                    } else if ( copy !== undefined ) {
                        target[ name ] = copy;
                    }
                }
            }
        }
    
        // Return the modified object
        return target;

    首先利用一个for循环来处理多个参数的情况,接着判断当前参数是否为null,如果为null的话,就不向下执行了。

    再接着是一个for循环,循环传入的参数,在这个循环里进行对当前这个参数的对象进行解析和扩展。

    首先对src和copy进行赋值

    src = target[ name ];
    copy = options[ name ];

    然后进行引用判断,判断要扩展的对象和被扩展的对象两者的引用是否相同,如果相同,则跳出,防止循环引用的情况发生。

    // Prevent never-ending loop
                    if ( target === copy ) {
                        continue;
                    }

    然后判断是否是深拷贝:

    // Recurse if we're merging plain objects or arrays
                    if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
                        if ( copyIsArray ) {
                            copyIsArray = false;
                            clone = src && jQuery.isArray(src) ? src : [];
    
                        } else {
                            clone = src && jQuery.isPlainObject(src) ? src : {};
                        }
    
                        // Never move original objects, clone them
                        target[ name ] = jQuery.extend( deep, clone, copy );
    
                    // Don't bring in undefined values
                    } else if ( copy !== undefined ) {
                        target[ name ] = copy;
                    }

    第一个判断,deep为true,也就是第一个参数为true,并且复制的对象不为空,还必须是对象或者数组,才可以进行深拷贝。

    接下来是对是否为数组进行判断,最重要的一句是

     clone = src && jQuery.isPlainObject(src) ? src : {};

    这里先判断src是否为空,如果不为空则把target[name]赋值到clone上,如果为空则传入一个空对象,这里是为了处理这种情况。

         var a = { tom: { sex: "man" }};
            var b = { tom: { age: 14 } }
            $.extend(true,a, b);
            console.log(a);

    当扩展对象和参数都有一个共同的对象时,那么正确做法是把参数b中不同的属性附加到a中,而不是进行覆盖。

    所以这里需要进行判断。

    如果这里将源码改了,也就是将:

    clone = src && jQuery.isPlainObject(src) ? src : {};  

    替换为

    clone =  {};

    只传入一个空对象,那么在次执行的结果为:

    可以看到,这里就是将两者的相同属性进行了覆盖操作,这样是不对的。

    最后进行递归调用,当深拷贝的时候,因为无法确定有几层,所以需要进行递归,直到最后一层。再次调用这个方法:

        // Never move original objects, clone them
                        target[ name ] = jQuery.extend( deep, clone, copy );

    这里深拷贝的代码就结束了,再看看浅拷贝,浅拷贝非常简单,只是在扩展对象上加一个对象进行赋值即可。

        // Don't bring in undefined values
                    } else if ( copy !== undefined ) {
                        target[ name ] = copy;
                    }

    最后返回target。

    // Return the modified object
        return target;

    extend的方法就结束了。

  • 相关阅读:
    pytest文档55-plugins插件开发
    pytest文档54-Hooks函数terminal打印测试结果(pytest_report_teststatus)
    Linux内存占用过高排查过程
    系统日志:/var/log/messages
    win10 安装oracle 11gR2_database(内附下载地址)
    CentOS 7 下 Docker 的离线安装方法
    docker 常用命令
    【docker】如何将服务器加入集群,成为子节点
    Docker管理面板系列——Portainer
    基于Docker的Consul服务发现集群搭建
  • 原文地址:https://www.cnblogs.com/y8932809/p/5863764.html
Copyright © 2020-2023  润新知