• 对jQuery.extend()方法的分析


    jQuery.extend方法是我们常用的方法,也是jQuery源码中的基础方法。它的主要作用是:将一个或多个“源对象”合并到一个“目标对象”中,并返回目标对象。它主要有三种表现形式:

    a、jQuery.extend(destination, source1, source2, source3 ....)

    b、jQuery.extend( source )

    c、jQuery.extend(boolean, destination, source1, source2, source3 ....)

    a方式第一个参数作为“目标对象”,其它参数作为“源对象”。

    b方式只有一个参数,这里的这个参数变成了“源对象”,“目标对象”变成了jQuery。说白了就是"源对象"的属性,变成jQuery函数的静态方法或属性。

    c方式的第一个参数是boolean类型的,第二个参数是"目标对象",剩下的参数是“源对象”。当第一个参数的值为true时,表示对象合并时支持“深拷贝”。

    知道了函数的用法,我们肯定好奇jQuery是怎么实现的,想看看jQuery的源码。不过在看jQuery源码之前,我们不妨试着写写这个方法的功能,然后在回过头来看jQuery源码,感受可能更深,看到的东西可能越多。

    我们先不要给自己压力,先从最简单的开始,要实现的方法就两个参数:第一个参数是:“目标对象”,第二个参数是:“源对象”。先实现把“源对象”合并到“目标对象”中。代码如下:

    var Test = function(){}
    Test.extend0 = function(destination, source){
        for(var key in source){
            destination[key] = source[key]
        }
        return destination
    }

    第二步实现可以传入多个参数,第一个参数是目标对象,其他参数是源对象。代码如下:

    Test.extend1 = function(){
        var destination = arguments[0]
        var sourceArr = Array.prototype.slice.call(arguments,1)
        for(var i = 0, len = sourceArr.length; i < len; i++){
            var source = sourceArr[i]
            for(var key in source){
                destination[key] = source[key]
            }
        }
        return destination
    }

    第三步实现只有一个参数时,将参数对象的属性附加给Test。代码如下:

    Test.extend2 = function(){
        var argumentsLen = arguments.length
        if( argumentsLen === 1 ){
            var source = arguments[0]
            for(var key in source){
                Test[key] = source[key]
            }
        }else{
            var destination = arguments[0]
            var sourceArr = Array.prototype.slice.call(arguments,1)
            for(var i = 0, len = sourceArr.length; i < len; i++){
                var source = sourceArr[i]
                for(var key in source){
                    destination[key] = source[key]
                }
            }
            return destination
        }
    }

    第四步实现“深拷贝”,第一个参数是是否进行深拷贝的布尔判断,第二个参数是目标对象,其他参数是源对象。代码如下:

    Test.extend3 = function(){
        var argumentsLen = arguments.length
        if( argumentsLen === 1 ){
            var source = arguments[0]
            for(var key in source){
                Test[key] = source[key]
            }
        }else{
            var firstItem = arguments[0]
            var isBoolean = typeof firstItem === "boolean"
            var destination = isBoolean ? arguments[1] : firstItem
            var startNum = isBoolean ? 2 : 1
            var sourceArr = Array.prototype.slice.call(arguments,startNum)
            for(var i = 0, len = sourceArr.length; i < len; i++){
                var source = sourceArr[i]
                if( isBoolean ){
                    deepExtend( destination, source )
                }else{
                    for(var key in source){
                        destination[key] = source[key]
                    }
                }
            }
            return destination
        }
    }
    
    function deepExtend(destination, source){
        for(var key in source){
            var value = source[key]
            if( value instanceof Array ){
                destination[key] = arguments.callee.call( destination[key] || [], value )
            }else if( value instanceof Object ){
                destination[key] = arguments.callee.call( destination[key] || {}, value )
            }else{
                destination[key] = source[key]                
            }
        }
        return destination
    }

    好了,我们按照自己的思路,粗略的实现了自己的extend方法,现在就看下jQuery对extend的实现,对比学习一下。源码如下:

    jQuery.extend = jQuery.fn.extend = function() {
        var src, copyIsArray, copy, name, options, clone,
            target = arguments[0] || {},
            i = 1,
            length = arguments.length,
            deep = false;
    
        // Handle a deep copy situation
        if ( typeof target === "boolean" ) {
            deep = target;
    
            // skip the boolean and the target
            target = arguments[ i ] || {};
            i++;
        }
    
        // Handle case when target is a string or something (possible in deep copy)
        if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
            target = {};
        }
    
        // extend jQuery itself if only one argument is passed
        if ( i === length ) {
            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;
    }

    通过对比,我们发现:

    a、jQuery在代码组织和实现上更加优雅。

    b、jQuery考虑到了一些特殊情况。比如:

    if ( target === copy ) {
          continue;
    }

    这是为了避免无限循环,“源对象”的属性指向的是“目标对象”,当合并对象时,也就是将“自己”复制为“自己的属性”。这是不可取的。

    c、jQuery在数组(jQuery.isArray)和“纯粹对象”(jQuery.isPlainObject)的判断上,考虑的更精细。

    先自己想思路去实现,再反过来对比学习,这种学习方法感觉挺好的。a、加强了独立思考能力。b、发现新的学习内容。c、暴漏自己的不足。

  • 相关阅读:
    笔记:一篇关于容器和虚拟机的对比
    语义化版本说明脑图
    KiCad EDA 5.1.4 发布了
    KiCad 5.1.4 无法覆铜?
    mac 常用的终端命令
    PC 商城扫描二维码登录
    Git的撤销与回滚
    springboot 集成elasticsearch5.4.3
    redis 缓存类型为map
    基于Elasticsearch 5.4.3的商品搜索系统
  • 原文地址:https://www.cnblogs.com/qiaotaojin/p/4057376.html
Copyright © 2020-2023  润新知