注意事項
- 區分引用類型和普通類型
- 注意循環引用的問題
- 區份內部變量和原型鏈中的變量
代碼
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