共计 864 个字符,预计需要花费 3 分钟才能阅读完成。
注意事项
- 区分引用类型和普通类型
- 注意循环引用的问题
- 区分内部变量和原型链中的变量
代码
function getType(arg) {return Object.prototype.toString.call(arg).replace(/\[object (.+)\]/,'$1')
}
function deepCopy(arg, map) {let typeID = ['Array','Object'].indexOf(getType(arg))
if (typeID < 0) return arg
let rtn = typeID ? {} : []
map = map || new WeakMap()
map.set(arg, rtn)
Object.keys(arg).map(item => {if (map.has(arg[item])) {rtn[item] = map.get(arg[item])
} else {rtn[item] = deepCopy(arg[item],map)
}
})
return rtn
}
解析
- 深拷贝的实现思路无外乎对基本类型直接返回,引用类型进行递归。
- 为了避免循环引用导致的栈溢出,使用 WeakMap 存储 被拷贝项 => 拷贝项 结构,由于 WeakMap 对于键名是弱引用,因此键名必须是引用类型,因此此处同时还能起到验证放入 map 中的项不是基础类型的作用。
- 对于遍历的每个对象,都先在 map 中寻找是否有对应的项,有则说明此项指向了之前遍历过的项,即存在循环引用,此时直接取出此 被拷贝项 对应的 拷贝项 的值进行赋值。
- 此处循环之所以采用 Object.keys(arg).map 而不用 for(let i in arg),是为了避免遍历出原型链中的变量。例如:
a={b:2};a.__proto__.c=4
console.log(Object.keys(a)) // [‘b’]
for(let i in a){console.log(i)} // b c
验证
a={b:{c:[]}};a.b.c[0]=a;;a.b.c[1]=NaN;a.b.c[2]=null;
b=deepCopy(a)
b.b.c[0]===b //true
正文完
发表至: javascript
2019-10-21