JS-深克隆

169 阅读1分钟

只有数组和对象的深克隆

deepClone方法

function deepClone(arr=undefined,spaceM=new Map()) {
    if (typeof (arr) == "object") {
        //区分递归中的arr是数组还是对象
        //用展开运算符先浅克隆一波
        let newArray = Array.isArray(arr) ? [...arr] : { ...arr };
        //代码A部分开始,用Map阻止循环引用的克隆,防止栈溢出。
        //其中key值保存的是arr,value值保存的是newArray,查看到map中已经有存储过的arr时,return终止进一步的for in循环
        if (spaceM.get(arr)) {
            return spaceM.get(arr);
        }
        spaceM.set(arr,newArray)
        //代码A部分结束
        for (const index in arr) {
            if (typeof (arr[index]) == "object") {//发现有引用对象,进行递归deepClone克隆
                newArray[index] = deepClone(arr[index],spaceM)
            }
        }
        return newArray
    } else {
        return arr
    }
}

Note

  1. 用展开运算符先浅克隆
  2. for in 循环查看浅克隆中的引用,通过递归再次克隆
  3. 在子递归中,新的newArray被return出来,即deepClone(arr[index], spaceM)会开辟一块新的空间,返回一个新的引用,赋值给newArray[index],这时深克隆完成
  4. 深克隆要考虑到克隆的对象存在循环引用情况,如A的next指向B,B的next指向A,通过Map阻止一直递归克隆。map最终的key是老的arr的各个引用节点,value则是新的arr的各个引用节点

测试

测试一

let space = new Map()
let arr = [
    // {a:1,b:2},
    2,
    [1, 2, 3],
    "fae"
]
let newArray = deepClone(arr, space)
console.log(newArray)
console.log(space)

测试二

let space = new Map()
let arr1 = {
    a: 1,
    b: 1,
    d: {
        a: 1,
        b: 1
    },
    c: 1,
    f: [
        { a: 1, b: 2 },
        2,
        [1, 2, 3],
        "fae"
    ]
}
let newArray = deepClone(arr1, space)
console.log(newArray)
console.log(space)

测试三

let space = new Map()
let A = {}
let B = {}
A.next = B
A.x = 12
B.next = A
B.x = 13
let newArray = deepClone(A, space)
console.log(newArray)
console.log(space)