实现一个完整的深拷贝

想到深拷贝,大概最简单的方法就是下面这个样子了吧...

JSON.parse(JSON.stringify(origin));

但是需要注意的是JSON的深拷贝会忽略属性值函数和symbol以及undefined之类的属性
下面给出一个比较完整的深拷贝
可以正确处理循环引用,以及属性为symbol类型的对象,是看到的一篇关于深拷贝的博客中提到的,自己先码住...
判断一个变量是数组或者是对象:

function isObject(source){
  return typeof source === 'object' && source !== null;
}
function cloneDeep4(source, hash = new WeakMap()) {

    //如果是原始类型就直接返回原始值
    if (!isObject(source)) return source; 
    if (hash.has(source)) return hash.get(source); 
      
    let target = Array.isArray(source) ? [] : {};
    hash.set(source, target);
    
    Reflect.ownKeys(source).forEach(key => { // 改动
        if (isObject(source[key])) {
            target[key] = cloneDeep4(source[key], hash); 
        } else {
            target[key] = source[key];
        }  
    });
    return target;
}

上面代码中的WeakMap是为了保存已经拷贝过的对象,防止对循环引用的对象重复拷贝;而Reflect.ownKeys函数的作用则是获取对象自己的所有属性,包含不可枚举的以及属性为symbol类型。

但是,对还是有但是,递归可能会导致爆栈的问题,所以可以使用循环来代替:

function cloneDeep5(x) {
    const root = {};

    // 栈
    const loopList = [
        {
            parent: root,
            key: undefined,
            data: x,
        }
    ];

    while(loopList.length) {
        // 广度优先
        const node = loopList.pop();
        const parent = node.parent;
        const key = node.key;
        const data = node.data;

        // 初始化赋值目标,key为undefined则拷贝到父元素,否则拷贝到子元素
        let res = parent;
        if (typeof key !== 'undefined') {
            res = parent[key] = {};
        }

        for(let k in data) {
            if (data.hasOwnProperty(k)) {
                if (isObject(data[k])) {
                    // 下一次循环
                    loopList.push({
                        parent: res,
                        key: k,
                        data: data[k],
                    });
                } else {
                    res[k] = data[k];
                }
            }
        }
    }

    return root;
}

你可能感兴趣的:(实现一个完整的深拷贝)