jQuery.extend()方法源码解析

1.变量的含义:

var target
    ,options /* 指向源对象 */
    ,name,   /*  键名 */
    ,i = 1   /* 指向的源对象在arguments中的索引 */
    ,length = arguments.length /* 参数的个数 */
    ,deep = false   /* 标志是否深拷贝,没有boolean值的时候默认为flase,浅复制 */
    ,src     /* 存储target对象的当前属性 */
    ,copy    /* 存储源对象当前属性的值 */
    ,target = arguments[0] || {} /* 过滤掉undefined 和 null */
    ,clone;  /* 深复制时候存放target[name]值 */
    ,copyIsArray; /* 存放一个标志,表示源对象当前属性值是否是数组 */

2.if代码块1:extend函数提供用户指定是否深拷贝的功能通过在第一个参数传递boolean值,检测函数第一个值是否是boolean值。

2.1. 第一个参数是boolean时候执行。

2.2. 第一个参数是boolean类型即是target的指向错了,i指向也错了,必须进行修改,并且重置deep标志。
/* 1.第一个参数是boolean时候执行,
   2.第一个参数是boolean类型即是target的指向错了,i指向也错了,必须进行修改,并且重置deep标志
*/

if ( typeof target === "boolean" ) {
    deep = target;
    target = arguments[1] || {}; /* 过滤第2个参数值为undefined,null */
    i = 2;
}

3.if代码块2:因为js中对那些不是对象不是函数的值设置属性方法是无效的,这里过滤这些值,保证target指向一个可以设置属性的对象。

3.1. if语句:满足target不是对象也不是函数时执行。

3.2. 满足条件的target值有可能是以字面量方式创建的boolean,number,string。

3.3. 不会出现target值为undefined和null,前面初始化时候已经过滤。
if ( typeof target !== "object" 
    && !jQuery.isFunction(target) ) {
    target = {};
}

4.if代码块3:extend()函数实现了当传入的参数有效对象个数不足够赋值给target和源对象时,默认将target指向自身。

4.1. arguments长度等于i的值得时候,表示target = arguments[arguments.length-1],
这时候的源对象是arguments[arguments.length] = undefined,显然这是没意义的。

4.2. jQuery在出现这种情况时,将target对象设置为自身,然后修改i指向target原先的指向。

4.3. 我们可以通过这个方法对jQuery进行扩展。
if ( length === i ) {
target = this;
--i;
}

5.for代码块,实现target对象扩展,从第一个源对象开始遍历arguments。

for ( ; i < length; i++ ) {

    /* 以上我们已经将arguments中的项过滤到target指向,以下if在源对象值为undefiend和null时进行过滤*/
    
    if ( (options = arguments[ i ]) != null ) {
        for ( name in options ) {
            src = target[ name ];
            copy = options[ name ];
            
            /* 为了避免在递归深拷贝过程中,出现了无限循环:(看后面代码片1,2)
               1.向extend()方法传入的源对象拥有属性值为目标对象,并且目标对象中有属性引用了目标对象本身,
               2.源对象的属性引用了源对象自身*/
               
            if ( target === copy ) {
                continue;
            }
        
            /* 判断是否满足深拷贝的条件:
            1.deep标志为true,
            2.源对象当前属性(name)的值存在,
            3.源对象当前属性值是数组,{}或者是由Object创建的对象*/
            
            if ( deep 
                 && copy 
                 && ( jQuery.isPlainObject(copy) 
                 || (copyIsArray = jQuery.isArray(copy)) ) ) {
            
                /* copyIsArray 是标示源对象当前属性值是否是数组,通过这个标志分别对源对象当前属性
                值为对象和源对象当前属性值为数组的情况分开处理*/
                
                if ( copyIsArray ) {
                
                    /* 如果不设置为false,下一次既使满足深拷贝的条件进入后,如果源对象当前属性值为对象,会按照数组进行处理*/
                    copyIsArray = false;
                    
                    /* clone存放了target[name]的值,值的如果不存在或者类型不是数组时,将其赋值空数组,否则值不改变 */
                    clone = src && jQuery.isArray(src) ? src : [];
                    
                    } else {
                    clone = src && jQuery.isPlainObject(src) ? src : {};
                    }
                    
                    /* 递归调用extend进行深拷贝*/
                    target[ name ] = jQuery.extend( deep, clone, copy );
                    
                    /* 过滤源对象属性值为undefined的属性,没有过滤属性值为null的属性,看以下代码片3*/
                } else if ( copy !== undefined ) {
                    target[ name ] = copy;
                }
            }
        }
    }
    
    /* 返回修改后的对象*/
    return target;
};

6.代码片1

/* 1.target对象属性值引用自身,并且original对象的属性引用target,注释if(target == copy)中的continue后无限循环最后
报错*/
/* 2.错误信息:Uncaught RangeError: Maximum call stack size exceeded(超过最大调用堆栈内存)*/
var target = {};
target.proper = target;
var original = {};
original.proper = target;
$.extend(true,target,original);
jQuery.extend()方法源码解析_第1张图片
注释continue

jQuery.extend()方法源码解析_第2张图片
运行结果

7.代码片2

 /*bug:
 2.original对象存在属性引用其自身,报错信息和上面一致 */
var target = {};
target.proper = '属性';
var original = {};
original.proper = original;
console.log($.extend(true,target,original));

8.代码片3

/* 过滤掉属性值为undefiend的属性,没过滤属性值为null的属性,可以测试以下代码:可以修改copy!= undefined来实现过滤null*/
var target = {}
var original = {
   name: null
}
console.log($.extend(target,original));

你可能感兴趣的:(jQuery.extend()方法源码解析)