大致过程:对后一个参数进行循环,然后把后面参数上所有的字段都给了第一个字段,若第一个参数里有相同的字段,则进行覆盖操作,否则就添加一个新的字段
1)对第一个参数做判断,如果不是Boolean类型,且只有一个参数,那么就把jQuery作为target,然后把第一个参数上的字段都赋给target,最后返回target(浅拷贝);如果有多于一个参数,那么第一个参数就是target,然后把后面参数的字段都赋给target,最后返回target(浅拷贝)
2)第一个参数是boolean类型,表示深浅拷贝,true表示深拷贝,false表示浅拷贝。只有两个参数,那么就把jQuery作为target,把第二个参数的字段赋给target,然后返回target。多于两个参数,把第二个参数作为target,然后把后面的参数的字段赋给target,最后面返回target.
具体思路:
1)保存第一个参数到target变量中,如果没有参数则target={},将变量i初始化为1,用变量length保存实际传参个数,变量deep初始化为false(用于判断是否为深度遍历)
2)接着判断第一个参数即target是否为boolean类型,如果是的话,将第一个参数即target赋值给deep,target的值变成第二个参数的值,如果没有第二个参数,那么target={},修改i=2。如果不是直接跳到第3)步
3)如果目标参数即target既不是Object也不是Function,那么target={}
4)如果目标参数target后面没有参数了(i=1或者第一个参数为boolean类型的有2个参数的情况,i = length判断),则目标对象target=this表示jQuery对象,而target表示的参数不在是目标对象,此时i–。【一个参数且为对象i=0;两个或以上对象参数i=1;有两个参数第一个参数为Boolean第二个为object,i=1;有两个或以上参数,第一个为Boolean,剩下的为object,i=2】
5)从下标i开始,对参数进行遍历, 如果参数不为空对象,那么对对象的属性进行遍历
a) 若参数中字段的值就是目标参数,停止赋值,防止无限的循环嵌套
var _default = {name : 'wenzi'};
var obj = {name : _default}
$.extend(_default, obj);
console.log(_default);
b) 若deep为true,且当前参数中name字段的值存在且为object类型或Array类型,则进行深度赋值
c) 若copy是简单的类型且存在值,则直接进行赋值
6)返回target
源码如下:
// 为与源码的下标对应上,我们把第一个参数称为`第0个参数`,依次类推
jQuery.extend = jQuery.fn.extend = function() {
var options,
name,
src,
copy,
copyIsArray,
clone,
target = arguments[0] || {}, // 默认第0个参数为目标参数
i = 1, // i表示从第几个参数开始将目标参数与其进行合并,默认从第1个参数开始向第0个参数进行合并
length = arguments.length,
deep = false; // 默认为浅拷贝
// 判断第0个参数的类型,若第0个参数是boolean类型,则获取其为true还是false
// 同时将第1个参数作为目标参数,i从当前目标参数的下一个
// Handle a deep copy situation
if ( typeof target === "boolean" ) {
deep = target;
// Skip the boolean and the target
// 如果第一个参数是Boolean类型
target = arguments[ i ] || {};
i++;
}
// 判断目标参数的类型,若目标参数既不是object类型,也不是function类型,则为目标参数重新赋值空对象
// Handle case when target is a string or something (possible in deep copy)
if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
target = {};
}
// 若目标参数后面没有参数了,如$.extend({_name:'wenzi'}), $.extend(true, {_name:'wenzi'})
// 则目标参数即为jQuery本身,而target表示的参数不再为目标参数
// Extend jQuery itself if only one argument is passed
if ( i === length ) {
target = this;
i--;
}
// 从第i个参数开始遍历
for ( ; i < length; i++ ) {
// 获取第i个参数,且该参数不为null和undefind,在js中null和undefined,如果不区分类型,是相等的,null==undefined为true,
// 因此可以用null来同时过滤掉null和undefind
// 比如$.extend(target, {}, null);中的第2个参数null是不参与合并的
// Only deal with non-null/undefined values
if ( (options = arguments[ i ]) != null ) {
// 使用for~in获取该参数中所有可枚举的属性
// Extend the base object
for ( name in options ) {
src = target[ name ]; // 目标参数中name字段的值
copy = options[ name ]; // 当前参数中name字段的值,有可能是值,Object,Array
// 若参数中属性的值就是目标参数,停止赋值,进行下一个字段的赋值
// 这是为了防止无限的循环嵌套,我们把这个称为,在下面进行比较详细的讲解
// Prevent never-ending loop
if ( target === copy ) {
continue;
}
// 若deep为true,且当前参数中name字段的值存在且为object类型或Array类型,则进行深度赋值
// Recurse if we're merging plain objects or arrays
// jQuery.isPlainObject()返回值为Boolean类型,如果指定的参数是纯粹的对象,则返回true,否则返回false
if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
// 若当前参数中name字段的值为Array类型
// 判断目标参数中name字段的值是否存在,若存在则使用原来的,否则进行初始化
if ( copyIsArray ) {
//被复制的属性的值类型为Array
copyIsArray = false;
//判断该字段在target对象是否存在,存在且为Array,则直接使用原对象,否则创建空的Array
clone = src && jQuery.isArray(src) ? src : [];
}else{
// 被复制的属性的值得类型为Object
//判断该字段在target对象是否存在,若存在且为Object则直接使用,否则创建空的Object
clone = src && jQuery.isPlainObject(src) ? src : {};
}
// 递归处理,此处为2.2
// Never move original objects, clone them
target[ name ] = jQuery.extend( deep, clone, copy );
// deep为false,则表示浅度拷贝,直接进行赋值
// Don't bring in undefined values
}else if ( copy !== undefined ) {
// 若copy是简单的类型且存在值,则直接进行赋值
// 若原对象存在name属性,则直接覆盖掉;若不存在,则创建新的属性
target[ name ] = copy;
}
}
}
}
// 返回修改后的目标参数
// Return the modified object
return target;
};