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);
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));