您的位置:

JS深拷贝

一、什么是深拷贝

在JS中,对象是通过引用传递的。当我们将一个对象赋值给另一个变量时,实际上是将对象的指针(引用地址)赋值给该变量,而非复制对象的内容。这意味着,当我们更改新变量的属性时,原始变量的属性也会被更改。

当我们需要复制一个对象,并确保修改副本不会对原始对象造成任何影响时,需要对对象进行深拷贝。深拷贝将克隆一个全新的对象,而不是指针地址。

二、浅拷贝的问题

我们先来看一个简单的例子:

let obj1 = {
    a: 1,
    b: {
        c: 2,
    },
};
let obj2 = obj1;

obj1.a = 10;
obj2.b.c = 20;

console.log(obj1);
console.log(obj2);

在这个例子中,我们通过赋值的方式创建了一个对象副本。我们发现,对obj2的修改也会影响到obj1,因为它们指向同一个对象。这就是浅拷贝的问题。

使用深拷贝可以解决这个问题。

三、常见的深拷贝方法

1. JSON.parse(JSON.stringify(object))

这是使用JSON的方法。我们先将对象序列化为JSON格式的字符串,然后再将其解析为新对象。这种方法可以很好地复制简单对象,并且很容易实现:

let obj1 = {
    a: 1,
    b: {
        c: 2,
    },
};
let obj2 = JSON.parse(JSON.stringify(obj1));

obj1.a = 10;
obj2.b.c = 20;

console.log(obj1);
console.log(obj2);

这种方法很简单,可是如果对象中包含函数、日期、正则表达式等特殊类型的数据,会出现意料之外的结果。例如:

let obj1 = {
    a: 1,
    b: function() {console.log('a');},
    c: new Date(),
    d: /123/,
};
let obj2 = JSON.parse(JSON.stringify(obj1));

console.log(obj1);
console.log(obj2);

这段代码将抛出错误,因为函数无法被序列化。

2. 递归复制对象

另一种方式是递归复制对象。我们首先创建一个新对象,然后遍历原始对象并递归复制其属性:

function deepCopy(obj) {
    let newObj = Array.isArray(obj) ? [] : {};
    if (obj && typeof obj === "object") {
        for (let key in obj) {
            if (obj.hasOwnProperty(key)) {
                newObj[key] = deepCopy(obj[key]);
            }
        }
    }
    return newObj;
}

let obj1 = {
    a: 1,
    b: {
        c: 2,
    },
};
let obj2 = deepCopy(obj1);

obj1.a = 10;
obj2.b.c = 20;

console.log(obj1);
console.log(obj2);

这种方法可以正确地复制对象和函数,并且能够处理循环引用,但是效率低下。

3. 使用第三方库

最后一种方式是使用第三方库,比如Lodash的深拷贝方法:

let obj1 = {
    a: 1,
    b: {
        c: 2,
    },
};
let obj2 = _.cloneDeep(obj1);

obj1.a = 10;
obj2.b.c = 20;

console.log(obj1);
console.log(obj2);

这种方法使用起来非常简单,而且可以处理各种情况。但是需要引入第三方库,增加了项目的依赖。

四、总结

深拷贝是保证对象副本独立于原始对象的必要手段。在选择深拷贝方法时,可以根据实际情况选择不同的方案。JSON.parse(JSON.stringify(object))可以简单地处理大多数简单对象,但是不支持函数、日期、正则表达式等类型。

递归复制对象可以处理各种类型的对象,但是效率较低。

使用第三方库可以处理各种情况,但是需要引入依赖。