浅拷贝
对象的浅拷贝是这样一种拷贝:它的属性与源对象(从中创建拷贝的对象)的属性共享相同的引用(指向相同的底层值)。因此,当你改变源对象或拷贝对象时,可能会导致另一个对象也随之改变。这种行为与深拷贝的行为形成对比,深拷贝中源对象和拷贝对象是完全独立的。
更正式地讲,如果以下条件成立,则两个对象o1和o2是浅拷贝:
- 它们不是同一个对象(
o1 !== o2)。 o1和o2的属性具有相同的名称,且顺序相同。- 它们属性的值相等。
- 它们的原型链相等。
另请参阅结构等价的定义。
如果一个对象的属性都具有原始值,那么它的拷贝符合深拷贝和浅拷贝的定义。然而,谈论这种拷贝的深度有些无意义,因为它没有嵌套属性,而我们通常在修改嵌套属性的背景下谈论深拷贝。
对于浅拷贝,只复制顶层属性,而不复制嵌套对象的值。因此:
- 重新赋值拷贝的顶层属性不会影响源对象。
- 重新赋值拷贝的嵌套对象属性会影响源对象。
在 JavaScript 中,所有标准的内置对象拷贝操作(展开语法、Array.prototype.concat()、Array.prototype.slice()、Array.from() 和 Object.assign())都会创建浅拷贝而不是深拷贝。
考虑以下示例,其中创建了一个ingredientsList数组对象,然后通过复制该ingredientsList对象创建了一个ingredientsListCopy对象。
js
const ingredientsList = ["noodles", { list: ["eggs", "flour", "water"] }];
const ingredientsListCopy = Array.from(ingredientsList);
console.log(ingredientsListCopy);
// ["noodles",{"list":["eggs","flour","water"]}]
重新赋值嵌套属性的值将在两个对象中都可见。
js
ingredientsListCopy[1].list = ["rice flour", "water"];
console.log(ingredientsList[1].list);
// Array [ "rice flour", "water" ]
重新赋值顶层属性的值(本例中为索引0)将仅在更改后的对象中可见。
js
ingredientsListCopy[0] = "rice noodles";
console.log(ingredientsList[0]); // noodles
console.log(JSON.stringify(ingredientsListCopy));
// ["rice noodles",{"list":["rice flour","water"]}]
console.log(JSON.stringify(ingredientsList));
// ["noodles",{"list":["rice flour","water"]}]