浅拷贝与深拷贝

24 阅读3分钟

浅拷贝

是创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址 ,但其中的引用类型属性仍然指向同一个内存地址,因此修改任何一个对象都可能影响到另一个。

浅拷贝的实现方式

1. Object.assign()

Object.assign() 方法可以用来浅拷贝对象。它接受一个目标对象和一个或多个源对象,然后将源对象的所有可枚举属性的值复制到目标对象。这使得我们可以很容易地创建一个浅拷贝。

// 原始对象
const obj = {
  name: 'libai',
  age: 30
};
// 浅拷贝对象
const shallowCopy = Object.assign({}, obj);

// 修改浅拷贝的值
shallowCopy.age = 31;

// 输出结果
console.log('Original Object:', obj); // { name: 'libai', age: 30 }
console.log('Shallow Copy:', shallowCopy); // { name: 'libai', age: 31 }

2. 扩展运算符(...

扩展运算符可以用来创建一个对象或数组的浅拷贝。它将一个对象或数组的元素解构并复制到新的实例中。

// 原始数组
const arr = [1, 2, 3];

// 浅拷贝数组
const shallowCopy = [...arr];

// 修改浅拷贝的值
shallowCopy[0] = 10;

// 输出结果
console.log('Original Array:', arr); // [1, 2, 3]
console.log('Shallow Copy Array:', shallowCopy); // [10, 2, 3

3.[].concat(arr)

// 原始数组
const arr = [1, 2, 3];

// 浅拷贝数组
const shallowCopy = [].concat(arr);

// 修改浅拷贝的值
shallowCopy[0] = 10;

// 输出结果
console.log('Original Array:', arr); // [1, 2, 3]
console.log('Shallow Copy Array:', shallowCopy); // [10, 2, 3]

4.arr.slice()

// 原始数组
const arr = [1, 2, 3];

// 浅拷贝数组
const shallowCopy = arr.slice(0);

// 修改浅拷贝的值
shallowCopy[0] = 10;

// 输出结果
console.log('Original Array:', arr); // [1, 2, 3]
console.log('Shallow Copy Array:', shallowCopy); // [10, 2, 3]

5. 手搓浅拷贝

自定义的shallowCopy函数通常会使用for...in循环遍历对象的属性,并利用hasOwnProperty检查属性是否属于对象本身,然后简单地复制这些属性到新对象中,不涉及深层次的递归。

function shallowCopy(obj) {
    let newobj = {};
    for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
            newobj[key] = obj[key];
        }
    }
    return newobj;
}
let obj = {
    a: 1,
    b: { n: 2 }
}
console.log(shallowCopy(obj));//{ a: 1, b: { n: 2 } }
obj.b.n=1
console.log(shallowCopy(obj));//{ a: 1, b: { n: 1 } }

深拷贝

是将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象。

深拷贝的实现方式

1.JSON.parse(JSON.stringify(obj))

1.1 无法拷贝undefinedfunctionSymbol属性

  • undefined的属性值会被忽略,因为它不是JSON格式的一部分。

  • 函数(function)作为对象的属性不能被序列化,所以在解析后会丢失。

  • Symbol作为键或值同样不会被处理,因为JSON.stringify会忽略Symbol类型的键,且Symbol值也不能被直接序列化。

1.2无法识别BigInt类型

1.3无法处理循环引用

如果对象结构中存在循环引用(即对象A的某个属性引用了对象B,同时对象B的某个属性又引用了对象A),JSON.stringify会抛出错误,因为它无法正确地序列化这样的结构。

2.自定义函数实现深拷贝**

实现深拷贝的关键就是使用递归复制原对象(或数组)的键和值,判断嵌套的值是否是引用类型(如下代码中的对象或数组),如果是,则继续递归,直到值是原始值为止。

image.png

3.structuredClone()

它是一种新的方法,用于创建一个对象或数组的深度克隆,包括其所有可枚举和不可枚举的属性,以及处理循环引用和特殊类型的值(如 MapSetDateRegExp 等)。

let obj = {
    a: 1,
    b: {n: 2}
}
const newObj = structuredClone(obj)
obj.b.n = 3
console.log(newObj) // { a: 1, b: { n: 2 } }