注意事项
- 区分引用类型和普通类型
- 注意循环引用的问题
- 区分内部变量和原型链中的变量
代码
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
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。