面试重点系列——深拷贝

184 阅读2分钟

你会的别人也会,为啥录取你?一样的题怎么把面试官撩到?

深拷贝

面试遇到这个问题,先说一下正常的思路:

  1. 进行遍历
  2. 遍历遇到值类型就赋值;
  3. 遇到引用类型:判断是Array还是Object,分别新建[]或{},进行递归;
  4. 返回结果。 这是90%的人都会回答出来的,你的竞争力在哪?

好的面试官会继续问

  1. 如果出现对象循环引用怎么办?
  2. 调用栈太多会爆掉,怎么解决?
  3. 没有亮眼的地方?

下面就解决这些问题:

如果出现对象循环引用怎么办?

使用WeakMap记录所有引用类型的数据。当存在相同引用时,直接用,不再递归,也解决了无限递归问题。

const map = new WeakMap();
if (map.has(origin[key])) {
    target[key]= map.get(origin[key]);
} else {
    target[key] = new origin[key].constructor();
    arr.push({origin: origin[key], target: target[key]});
}

调用栈太多会爆掉,怎么解决?

使用while+数组解决调用栈太长问题。这个方法是非常普遍的,如果面试遇到优化递归的问题,首先考虑while+数组的方法。

解释:遇到对象就push进数组。使用while循环数组,每次取数组的第一项(同时删除这一项),进行遍历操作。当数组为空时,就停止。

const arr = [{origin, target}];
while(tar = arr.shift()) {
    ...
    if (map.has(origin[key])) {
        target[key]= map.get(origin[key]);
    } else {
        target[key] = new origin[key].constructor();
        arr.push({origin: origin[key], target: target[key]});
    }
    ...
}

亮眼的地方?

  • 使用new origin.constructor() 创建对象。既不用再区分对象和数组,又向面试官展示了你对原型的理解和灵活运用。岂不美哉!!!
  • 使用WeakMap记录出现过的对象,体现你对弱引用的理解。

全部代码

const obj = {
    a: 1,
    b: {
        c: {
            d: 1
        },
    },
    c: [1, 2, 4]
}

function deepClone(origin, target = {}) {
    if (origin === null) return null;
    const map = new WeakMap();
    const arr = [{origin, target}];
    let tar;
    while(tar = arr.shift()) {
        const {origin, target} = tar;
        for (let key in origin) {
            if (Object.hasOwnProperty.call(origin, key)) {
                if (typeof(origin[key]) === 'object' && origin[key] !== null) {
                    if (map.has(origin[key])) {
                        target[key]= map.get(origin[key]);
                    } else {
                        target[key] = new origin[key].constructor();
                        arr.push({origin: origin[key], target: target[key]});
                    }
                } else {
                    target[key] = origin[key];
                }
            }
        }
    }
    return target;
}
console.log(deepClone(obj));