拷贝
//判断数据类型
function toType(obj){
let classType = {},
toString = classType.toString // => Object.prototype.toString
// 设置数据类型映射
let typeArr = ['Boolean', 'Number', 'String', 'Function', 'Array', 'Date', 'RegExp', 'Object', 'Error', 'Symbol']
typeArr.forEach(name => {
classType[`[object ${name}]`] = name.toLowerCase()
})
if(obj == null) return obj + '' // 检测null、undefined
return typeof obj === 'object' || typeof obj === 'function' ? classType[toString.call(obj)] || 'object' : typeof obj
}
//对象和数组的遍历
function each(obj, callback){
if (toType(obj) === 'array') {
for (let i = 0; i < obj.length; i++) {
callback(obj[i], i)
}
}
if (toType(obj) === 'object') {
for (const key in obj) {
console.log('key:', key);
callback(obj[key], key)
}
}
return obj
}
//对象的浅拷贝
function objectAndArrayShallowClone(obj){
// {...obj} // value值为null或者undefined时属性会丢失
let newObj = new obj.constructor
let keys = Object.keys(obj).concat(Object.getOwnPropertySymbols(obj))
each(keys, item => {
newObj[item] = obj[item]
})
return newObj
}
// 浅克隆
function shallowClone(obj){
let type = toType(obj),
Ctor = obj.constructor; //当前对象的构造函数
//Symbol
if(/^(symbol)$/i.test(type)) return Object(obj)
// RegExp Date
if(/^(regexp|date)$/i.test(type)) return new Ctor(obj)
// 错误类型
if(/^(error)$/i.test(type)) return new Error(obj.message)
//对于函数
if (/^function$/i.test(type)) {
return function(){
return obj.call(this, ...arguments)
}
}
// 数组或者对象
if(/^(object|array)$/i.test(type)){ //是数组或者对象
let result = objectAndArrayShallowClone(obj)
return result
}
//基础数据类型
return obj
}
//深克隆:只要有下一级 我们就克隆一次(浅克隆)
function deepClone(obj, cache= new Set()){ // cache 套娃深拷贝的处理 obj = {0: obj}
if (!obj) { // null\undefined
return obj
}
let type = toType(obj),
Ctor = obj.constructor;
if (!/^(object|array)$/i.test(type)) return shallowClone(obj)
//避免无限套娃
if (cache.has(obj)) return obj
cache.push(obj)
let newObj = new Ctor(),
keys = Object.keys(obj).concat(Object.getOwnPropertySymbols(obj))
each(keys, item => {
//再次调用deepClone的时候,把cache传递进去,保证每一次递归都是一个cache
newObj[item] = deepClone(obj[item], cache)
})
return newObj
}
merge
let options = Object.assign(oldOptions, newOptions)
let options = {...oldOptions, ...newOptions}
上面两种方法不足:相同类型的同名属性 后者直接覆盖前者
正确合并
-
几种情况的分析
-
A-> obj中的key值, B-> target中的key值
-
1、A&B都是原始值,B替换A即可
-
2、A是对象 B是原始值 抛出异常信息
-
3、A是原始值 B是对象 B替换A即可
-
4、A&B都是对象:依次遍历B中的每一项,替换A中的内容
function merge(obj, target={}){ each(target, (_,key) => { if (isObj(obj[key]) && !isObj(target[key])) throw new Error(`${key} in target must be object`) if (isObj(obj[key]) && isObj(target[key])){ obj[key] = merge(obj[key], target[key]) return } obj[key] = target[key] }) return obj } function isObj(val){ return toType(val) === 'object' }