【jQuery源码浅析】(三)--jQuery插件拓展--$.extend

前言

本文主要讲的不是如何使用jQuery的插件方法,而是jQuery.extendjQuery.fn.extend的实现,这两种方法实现了拓展jQuery方法的途径,前者是增加jQuery的静态方法,也就是$.do()这样;后者是增加jQuery的实例方法,也就是$(selector).do()这样。这两者的用法分别为$.extend$.fn.extend。如果想了解jQuery的插件开发可以看这里如何制作一个最简单的jQuery插件

这两个方法最神奇的地方是居然用同一个函数来实现,其实从源码中可知this在里面起到了巨大的作用。要了解这两个方法的实现方式,就要先从方法的实现效果去反推和理解。

核心源码

    // jQuery插件拓展
195     jQuery.extend = jQuery.fn.extend = function() {
            var options, name, src, copy, copyIsArray, 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;
262     };

思路分析

  1. 简单目标:jQueryjQuery.fn方法的拓展,也就是$.do()$().do()
  2. 中等目标:把所有的对象合并到第一个对象target上面(如果只有一个对象就合并到自己身上,等于没有合并),并将target返回到方法外。
  3. 高级目标:深拷贝,也就是说把每个对象中的所有对象的属性都拷贝到第一个对象身上,从object原型继承的属性会被拷贝,值为undefined的属性不会被拷贝。$.extend()的深拷贝和浅拷贝详细讲解

源码分析

1)明确target是哪个对象

        // 是用户传进来的第一个参数,如果不填,则新建一个对象(不能为空)
        var target = arguments[0] || {};

        // 如果第一个参数类型为boolean,则target转为第二个参数,如果不填,则新建一个对象(不能为空)
        if (typeof target === "boolean") {
            target = arguments[1] || {};
        }

        // 如果第一个参数不是对象或方法(也就有可能为字符串或其他),则将target转换成空对象
        if (typeof target !== "object" && !jQuery.isFunction(target)) {
            target = {};
        }

        // 如果只有一个参数,表示是 jQuery 静态方法
        if (length === i) { // length = arguments.length
            target = this;  // this指jQuery本身
                            // 所以$.extend和$.fn.extend可以写在一起,就是由这个this决定的
        }

        // 最后的target一定是对象,要么是用户传进来的对象,要么是空对象{}

2)复制所有对象的属性到第一个对象target的属性中,如果有相同属性名,将会覆盖target的该属性名的值

        // 如果target=this,则循环 1 次,返回原本的target
        // 如果target!=this,则循环 n-1 次,n为obj的数量(包括target本身)
        for (; i < length; i++) {
            // 取出每个对象的key,如果用es6,可以直接用for-of
            options = arguments[i]
            for (name in options) {
                if () {}
                else if (copy !== undefined) {
                    target[name] = copy;
                }
            }
        }

3)深克隆,将所有对象的属性复制到target中,如果属性不是对象,则完全覆盖;如果是纯对象或者是数组,则不完全覆盖,只覆盖对象或数组里面的相同属性。

        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 : {};
            }

            // 递归
            target[ name ] = jQuery.extend( deep, clone, copy );

        // 最终会走到这里,如果是undefined,则直接退出
        } else if ( copy !== undefined ) {
            target[ name ] = copy;
        }

最后总结

看完源码,感觉jQuery的extend方法太强大了,一个方法里面主要实现了两个目标,一是帮助jQuery和jQuery的实例增加方法,二是克隆对象。前者只能有一个参数,而后者可能有无限个参数。前者靠target = this来实现,后者靠 递归 来实现。总之,用85行的代码(含注释和空行)就能同时实现两个功能,既强大又简洁,完全符合了jQuery的 write less, do more. 的追求。

相关资料

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值