實現一個簡短實用的深拷貝

注意事項

  1. 區分引用類型和普通類型
  2. 注意循環引用的問題
  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
}

解析

  1. 深拷貝的實現思路無外乎對基本類型直接返回,引用類型進行遞歸。
  2. 爲了不循環引用致使的棧溢出,使用WeakMap存儲 被拷貝項=>拷貝項 結構,因爲WeakMap對於鍵名是弱引用,所以鍵名必須是引用類型,所以此處同時還能起到驗證放入map中的項不是基礎類型的做用。
  3. 對於遍歷的每一個對象,都先在map中尋找是否有對應的項,有則說明此項指向了以前遍歷過的項,即存在循環引用,此時直接取出此 被拷貝項 對應的 拷貝項 的值進行賦值。
  4. 此處循環之因此採用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
相關文章
相關標籤/搜索