深入解析 JS 深拷贝中的循环引用问题

2025-12-27 7488阅读

在 JavaScript 中,深拷贝是一个常见的需求,但循环引用却常常给深拷贝带来困扰。

深拷贝旨在创建一个与原对象完全独立的副本,包括其所有嵌套的属性和子对象。然而,当对象之间存在循环引用时,普通的深拷贝方法可能会陷入无限循环。

例如,有如下两个对象相互引用:

let obj1 = { name: 'obj1' };
let obj2 = { name: 'obj2' };
obj1.related = obj2;
obj2.related = obj1;

如果使用简单的递归深拷贝函数:

function deepCopy(obj) {
    let copy = {};
    for (let key in obj) {
        if (typeof obj[key] === 'object') {
            copy[key] = deepCopy(obj[key]);
        } else {
            copy[key] = obj[key];
        }
    }
    return copy;
}

在处理上述具有循环引用的对象时,就会出现栈溢出错误。因为函数会不断递归调用自身,试图拷贝相互引用的对象,却无法终止。

为了解决循环引用问题,可以使用额外的数据结构来记录已经拷贝过的对象。比如使用一个 WeakMap:

const visited = new WeakMap();
function safeDeepCopy(obj) {
    if (visited.has(obj)) {
        return visited.get(obj);
    }
    let copy;
    if (typeof obj === 'object') {
        if (obj instanceof Array) {
            copy = [];
        } else {
            copy = {};
        }
        visited.set(obj, copy);
        for (let key in obj) {
            if (typeof obj[key] === 'object') {
                copy[key] = safeDeepCopy(obj[key]);
            } else {
                copy[key] = obj[key];
            }
        }
    } else {
        copy = obj;
    }
    return copy;
}

通过这种方式,当遇到已经拷贝过的对象时,直接返回已有的副本,避免了循环引用导致的无限递归,从而实现安全的深拷贝。

总之,在 JS 深拷贝中,循环引用是一个需要特别关注和妥善处理的问题,合理运用额外的数据结构能有效解决这一难题。

文章版权声明:除非注明,否则均为Dark零点博客原创文章,转载或复制请以超链接形式并注明出处。