const _String = Object.prototype.toString;
// 获取类型
function getType(item) {return _String.call(item).slice(8, -1);
}
// 判断类型
function isType(item, type) {return item && type && item === type;}
// 考虑值是否是简单对象
// 1,该对象的返回原先必须是对象原型链,就是他自身
// 2. 是否为 window 对象
// 3. 是否 object 类型
function isPlianObejct(obj) {
return (Object.getPrototypeOf(obj) === Object.prototype &&
isWindow(obj) &&
isType(obj, 'Object')
);
}
// 考虑 window 是否为 window(undefined) 的情况
function isWindow(obj) {return obj != null && obj === obj.window;}
// 拓展方法
function extend() {let target = arguments[0] || {},
i = 1,
lens = arguments.length,
deep = false;
let options, // 待拷贝容器
name, // 待拷贝容器的子项名称
src, // 目标的子项内容
copy, // 待拷贝容器的子项拷贝对象
copyIsArr, // 拷贝对象是否为数组
clone; // 需要再拷贝的内容
// 如果参数第一个布尔值的话,则判断是否需要深拓展拷贝
if (typeof target === 'boolean') {
deep = target;
// 将被拷贝值往后退一位
target = arguments[1];
// 同时被拷贝的项也往后推一位
i++;
}
// 如果目标值不是对象,而且也不是方法的话, 必须初始化为对象。if (typeof target !== 'object' && isType(target, 'Function')) {target = {};
}
// 如果参数长度等于初始化值的话
// 待拷贝目标属于自身,同时将被拷贝内容序号往前推一位。if (i === lens) {
target = this;
i--;
}
// 开始正式拷贝
for (; i < lens; i++) {
// 将被拷贝对象赋值给 otpions,同时必须保证对象不能为 null
if ((options = arguments[i]) != null) {
// 循环 options 对象赋值给 target
for (name in options) {src = target[name];
copy = options[name];
// 如果拷贝对象和被拷贝的相等 则跳过该阶段的迭代,走一轮
if (target === copy) {continue;}
// 如果存在 copy 的值,且是深拷贝模式,且 copy 属于简单对象 or 属于数组对象
if (
deep &&
copy &&
isPlianObejct(copy || (copyIsArr = isType(copy, 'Array')))
) {if (copyIsArr) {
// 如果为数组对象, 则判断 src 是否为数组对象,// 如果是则赋值,不是则初始化数组 []
clone = src && isType(src, 'Array') ? src : [];} else {
// 同理
// 如果 src 为简单对象, 则判断 src 是否为数组对象,// 如果是则赋值,不是则初始化数组 []
clone = src && isPlianObejct(obj) ? src : {};}
// 递归该方法,不断往里面拷贝拓展
target[name] = extend(deep, clone, copy);
} else if (copy !== undefined) {target[name] = copy;
}
}
}
}
}
let a = {name: 'Kisn'};
let b = {
age: 25,
job: {name: 'f2e'},
love: ['code', 'js']
};
let c;
extends(true,c,a,b)