您的位置:

object.assign()的深拷贝还是浅拷贝?

一、参数对象为基本数据类型时

在调用object.assign()时,如果传入的参数对象为基本数据类型(number、string、boolean、null、undefined、NaN),则是浅拷贝。例如:

    
let obj1 = {a: 1, b: {c: 2}};
let obj2 = Object.assign({}, obj1);
obj1.b.c = 3;
console.log(obj2.b.c); // output: 3

上面的例子中,我们先定义了一个包含基本类型和对象类型的对象obj1,然后用object.assign()将obj1浅拷贝到一个空对象中。接着,我们改变了obj1中嵌套对象b的属性c的值,输出obj2中对应的属性c的值,发现也被改变了。

由此可以看出,因为obj1和obj2中的嵌套对象是同一个引用,所以改变一个也会影响其他的。

二、参数对象中有嵌套对象或数组

如果参数对象中有嵌套的对象或数组,那么使用object.assign()时会出现浅拷贝的情况。例如:

    
let obj1 = {a: 1, b: {c: 2}};
let obj2 = Object.assign({}, obj1);
obj1.b.d = [3, 4];
console.log(obj2.b.d); // output: [3, 4]

上面的例子中,我们先定义了一个包含基本类型和嵌套对象的对象obj1,然后用object.assign()将obj1浅拷贝到一个空对象中。接着,我们改变了obj1中嵌套对象b的属性d的值,输出obj2中对应的属性d的值,发现也被改变了。

由此可以看出,引用类型的赋值实际上是将指针复制一份,指向同一个地址,因此改变其中一个对象的嵌套属性或数组将会影响到另一个对象。

三、深拷贝的实现方法

在实际应用中,如果我们需要对嵌套对象或数组进行拷贝,需要使用深拷贝的方式。下面介绍几种常见的深拷贝实现方法:

1. JSON.parse()和JSON.stringify()

这是最常见的一种深拷贝方法,基于JSON.parse()解析字符串以及JSON.stringify()将对象转为字符串的特性,实现嵌套对象或数组的深拷贝。例如:

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

上面的例子中,我们先定义了一个包含基本类型和嵌套对象的对象obj1,然后使用JSON.parse()和JSON.stringify()对其进行深拷贝。接着,我们改变了obj1中嵌套对象b的属性c的值,输出obj2中对应的属性c的值,发现没有被改变。

但是需要注意的是,这种方法有以下缺点:

  • 只能处理JS支持的数据类型,即不能处理RegExp、Date实例、函数等非JSON数据类型。
  • 忽略undefined和Symbol类型的属性。
  • 不能实现循环引用的深拷贝,否则会抛出错误。

2. 递归拷贝

递归拷贝是指遍历对象或数组,对其中的嵌套对象或数组使用深拷贝的方式逐一拷贝,最终实现完全复制。例如:

    
function deepCopy(obj) {
    if(typeof obj !== 'object') {
        return obj;
    }
    let newObj = Array.isArray(obj) ? [] : {};
    for(let i in obj) {
        newObj[i] = deepCopy(obj[i]);
    }
    return newObj;
}
let obj1 = {a: 1, b: {c: 2}};
let obj2 = deepCopy(obj1);
obj1.b.c = 3;
console.log(obj2.b.c); // output: 2

上面的例子中,我们先定义了一个包含基本类型和嵌套对象的对象obj1,然后使用递归拷贝函数deepCopy()对其进行深拷贝。接着,我们改变了obj1中嵌套对象b的属性c的值,输出obj2中对应的属性c的值,发现没有被改变。

递归拷贝的好处是可以完全复制对象、数组以及其中包含的各种数据类型,没有限制,但是由于递归过程中会创建大量的对象或数组,因此如果拷贝的对象比较大或嵌套层数较深时,可能会影响性能。

3. Lodash的_.cloneDeep()

Lodash是一个实用的JS工具库,提供了丰富的函数,包括深拷贝的函数_.cloneDeep()。该函数可以对任何数据类型进行深拷贝,支持循环引用的拷贝,是比较常用的深拷贝方法之一。例如:

    
const _ = require('lodash');
let obj1 = {a: 1, b: {c: 2}};
let obj2 = _.cloneDeep(obj1);
obj1.b.c = 3;
console.log(obj2.b.c); // output: 2

上面的例子中,我们先定义了一个包含基本类型和嵌套对象的对象obj1,然后使用Lodash的_.cloneDeep()对其进行深拷贝。接着,我们改变了obj1中嵌套对象b的属性c的值,输出obj2中对应的属性c的值,发现没有被改变。

需要注意的是,使用Lodash需要先安装Lodash库,在Node.js中使用方式例如上面的代码。