js深拷贝与循环引用


在 JavaScript 中,深拷贝(deep copy)意味着创建一个新的对象,并递归地复制原始对象的所有属性,包括嵌套的对象和数组。深拷贝确保原始对象和副本之间不共享任何引用,因此修改副本不会影响原始对象。

然而,当原始对象中存在循环引用时,深拷贝的实现可能会变得复杂。循环引用是指两个或多个对象相互引用,形成一个闭环。在这种情况下,深拷贝函数需要能够识别并处理循环引用,以避免无限循环。

function deepCopy(target) {
    var arr = []; // use array store the used refrence, and avoid repeat
    return _deepCopy(target);
    
    function _deepCopy(target) {
        let result = {};
        for (let k in target) {
            if (target.hasOwnProperty(k)) {
                if (target[k] && typeof target[k] === 'object') {
                    if ((arr.filter(v=>v === target[k])).length === 0) {
                        arr.push(target[k]);
                        result[k] = _deepCopy(target[k]);
                    }

                } else {
                    result[k] = target[k];
                }
            }
        }
        return result;
    }
}

// test code
var a = {
    n: 1,
    m: 2
};
var b = {
    m: 1,
    n: 2
};
a.b = b;
b.a = a;
var c = deepCopy(a);

另外,我们可以在深拷贝函数中使用一个映射(如 Map 或 WeakMap)来跟踪已经拷贝过的对象。以下是一个处理循环引用的深拷贝函数的示例:

function deepCopy(obj, map = new WeakMap()) {
    if (obj === null) return null; // 处理 null 值
    if (typeof obj !== 'object') return obj; // 处理非对象值
    if (map.has(obj)) return map.get(obj); // 处理循环引用

    const clone = Array.isArray(obj) ? [] : {};
    map.set(obj, clone); // 记录当前对象的副本

    Object.keys(obj).forEach((key) => {
        clone[key] = deepCopy(obj[key], map); // 递归拷贝对象的属性
    });

    return clone;
}

增加对 RegExp和Date类型的处理

function deepCopy(target) {
    var arr = [];
    return _deepCopy(target);

    function _deepCopy(target) {
        let result = {};
        for (let k in target) {
            if (target.hasOwnProperty(k)) {
                let obj = target[k];
                if (obj && typeof obj === 'object') {
                    if ((arr.filter(v=>v === obj)).length === 0) {
                        arr.push(obj);

                        // copy RegExp
                        if (obj instanceof RegExp) {
                            result[k] = new RegExp(obj);
                        } 
                        // copy Date
                        else if (obj instanceof Date) {
                            result[k] = new Date(obj);
                        } else {
                            result[k] = _deepCopy(target[k]);
                        }

                    }

                } else {
                    result[k] = target[k];
                }
            }
        }
        return result;
    }
}

var a = {
    n: 1,
    m: 2
};
var b = {
    m: 1,
    n: 2,
    r: /a/,
    d: new Date()
};
a.b = b;
b.a = a;
var c = deepCopy(a);

WeakMap去重版

// WeakMap 
function deepCopy(target) {
    var hash = new WeakMap(); // use WeakMap store the used refrence, and avoid repeat
    return _deepCopy(target);
    
    function _deepCopy(target) {
        let result = {};
        for (let k in target) {
            if (target.hasOwnProperty(k)) {
                let obj = target[k];
                if (obj && typeof obj === 'object') {
                    if ( !hash.has(obj)) {
                        hash.set(obj, 1);
                        // copy RegExp
                        if (obj instanceof RegExp) {
                            result[k] = new RegExp(obj);
                        } 
                        // copy Date
                        else if (obj instanceof Date) {
                            result[k] = new Date(obj);
                        } else {
                            result[k] = _deepCopy(target[k]);
                        }

                    }

                } else {
                    result[k] = target[k];
                }
            }
        }
        return result;
    }
}

深拷贝与浅拷贝与不拷贝

假设有个对象 let a = {a: { a: 1}}; 我们要生成另外一个对象b;

  1. 不拷贝: let b = a; 这是不拷贝,因为b和a是同一个引用,没有任何拷贝操作发生;如果给b添加一个新属性b.b=1;; 那么对象a也会添加一个新属性b且 a.b===1;
“拷贝”
  1. 浅拷贝 浅拷贝

  2. 深拷贝 不再解释了。

添加对数组的处理

function deepCopy(target) {
    var hash = new WeakMap();
    // use WeakMap store the used refrence, and avoid repeat
    return _deepCopy(target);

    function _deepCopy(target) {
        let result = {};
        for (let k in target) {
            if (target.hasOwnProperty(k)) {
                let obj = target[k];
                if (obj && typeof obj === 'object') {
                    if (!hash.has(obj)) {
                        hash.set(obj, 1);
                        // copy RegExp
                        if (obj instanceof RegExp) {
                            result[k] = new RegExp(obj);
                        }// copy Date
                        else if (obj instanceof Date) {
                            result[k] = new Date(obj);
                        }  // copy Array
                        else if (obj instanceof Array) {
                            let arr = [];
                            obj.forEach(v=>{
                                arr.push(deepCopy(v));
                            }
                            );
                            result[k] = arr;
                        } else {
                            result[k] = _deepCopy(target[k]);
                        }

                    }

                } else {
                    result[k] = target[k];
                }
            }
        }
        return result;
    }
}