继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

jQuery源码解析系列之$.extend 函数及用法详解

串猪神
关注TA
已关注
手记 4
粉丝 36
获赞 70

最近在学习jQuery源码(v2.0.3),故在此跟大家一起分享下学习的经验和一些小小技巧,jQuery的扩展方法extend是我们经常在写插件的时候常用的方法,extend方法有两种,一种是$.extend(),另外一种是$.fn.extend()。两种方法有少许差异,再次,我们一起去学习了解。

jQuery的扩展方法原型

extend(dest, src1, src2, src3...)

extend的含义是讲src1,src2,src3...合并到dest中,返回值为合并后的dest,从上面可以看出使用了extend方法后,会修改dest的结构,同时也可以将空对象{}作为参数传入,即使用如下:

var newSrc = $.extend({}, {name: 'ccc', age: 21}, {name: 'xxx, sex: 'Boy'});

可以得到合并后的结果如下所示:

newSrc = {name: 'xxx', age: 21, sex: 'Boy'};

可以看出如果多个合并对象中有相同属性的将会进行合并,并且后来合并的属性会覆盖前面的属性。

省略dest参数

同时extend方法的dest参数可以省略,如果省略了dest参数,那么extend方法只能拥有一个参数,要不然会报错,只有一个参数的时候是将该src合并到调用extend方法的对象中去,如下所示:

1、$.extend(src)

该方法就是将src合并到jQuery的全局对象中去,如:

$.extend({
    name: function () {
        console.log('my name is chuanzhushen');
    }
});

就是将name方法合并到jquery的全局对象中去,既可以通过$.name()或者jQuery.name()来调用此方法。

2、$.fn.extend(src)

该方法将src合并到jQuery的实例对象中去,如:

$.fn.extend({
    name: function () {
        console.log('my name is scienceswork');
    }
});

就是将name方法合并到jQuery的实例对象上去,该方法与$.extend()的区别在于需要实例化后才能调用该方法,如:

$().name(); // my name is scienceswork
$.name();   // my name is chuanzhushen

extend方法的重载原型

extend(boolean, dest, src1, src2, src3...)

第一个参数boolean代表是否进行深拷贝,其余参数与之前的相同,JavaScript里有深拷贝和浅拷贝,我们先来了解一下什么是深拷贝,什么是浅拷贝。

var result = $.extend(true, {}, 
    {name: 'ccc', location: {city: 'guangzhou', county: 'cn'}},
    {last: 'sciences', location: {state: 'xxx', county: 'cn'}}
)

从上面的例子中我们可以看出src1中嵌套子对象location:{city: 'guangzhou'},src2中也嵌套了对象localtion:{state: 'xxx'},第一个深度拷贝的参数我们发现为true,那么合并后的结果就是:

result = {
    name: 'ccc',
    last: 'sciences',
    location: {
        city: 'guangzhou',
        state: 'xxx',
        county: 'cn'
    }
};

上面即是使用浅拷贝进行合并的结果,其嵌套的对象不会进行深度遍历拷贝。

源码阅读

jQuery(v2.0.3)关于extend方法的源码从285行~347行,我们将其拷贝上来,并且做了简单的注释,对其进行一个分析:

jQuery.extend = jQuery.fn.extend = function() {
    // target = arguments[0] || {}表示是否有深度拷贝参数,如果没有的话就为空对象
    // length表示传入参数的长度,默认的深度拷贝参数deep为false
    var options, name, src, copy, copyIsArray, clone,
        target = arguments[0] || {},
        i = 1,
        length = arguments.length,
        deep = false;

    // 判断是否为深度拷贝参数,如果是的话赋值给deep,并且设置拷贝目标为arguments[1]
    // 且修改类数组的长度i为2
    if ( typeof target === "boolean" ) {
        deep = target;
        target = arguments[1] || {};
        // skip the boolean and the target
        i = 2;
    }

    // 判断传入的拷贝参数是否为对象或者函数,如果不是则直接设置为空对象{}
    if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
        target = {};
    }

    // 判断参数长度是否等于i
    if ( length === i ) {
        target = this;
        --i;
    }

    // 使用for循环进行拷贝
    for ( ; i < length; i++ ) {
        // 如果传入的拷贝参数为null,则不进行处理
        if ( (options = arguments[ i ]) != null ) {
            // 对被拷贝的对象进行遍历
            for ( name in options ) {
                src = target[ name ];
                copy = options[ name ];

                // 对target和copy进行判断,防止死循环
                if ( target === copy ) {
                    continue;
                }

                // 判断是否是深度拷贝,并且进行相应的操作
                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;
                }
            }
        }
    }

    // 返回对象
    return target;
};

从上可以看出,在内部使用了jQuery.extend = jQuery.fn.extend,在这里可以看出$.extend和$.fn.extend是一样的,为什么一个是静态方法,一个必须实例化后才能调用方法,在源码的第96行,可以看到使用了如下:

jQuery.fn = jQuery.prototype = {...}

表明jQuery将原形prototype赋值给了jQuery.fn,这也能表明为什么一个是静态方法,另外一个必须实例化对象后才能使用。部分解释已经写在注释里了,由于第一次解读源码,对源码的熟悉程度也没从整体上进行把握,欢迎大家来吐槽。一起学习一起进步。
接下来还会对jQuery源码进行其他方面的阅读,阅读源码是一个不错的学习方式,有志同道合的童鞋可以一起学习。

打开App,阅读手记
7人推荐
发表评论
随时随地看视频慕课网APP