浅拷贝

对象的浅拷贝是这样一种拷贝:它的属性与源对象(从中创建拷贝的对象)的属性共享相同的引用(指向相同的底层值)。因此,当你改变源对象或拷贝对象时,可能会导致另一个对象也随之改变。这种行为与深拷贝的行为形成对比,深拷贝中源对象和拷贝对象是完全独立的。

更正式地讲,如果以下条件成立,则两个对象o1o2是浅拷贝:

  1. 它们不是同一个对象(o1 !== o2)。
  2. o1o2的属性具有相同的名称,且顺序相同。
  3. 它们属性的值相等。
  4. 它们的原型链相等。

另请参阅结构等价的定义。

如果一个对象的属性都具有原始值,那么它的拷贝符合深拷贝和浅拷贝的定义。然而,谈论这种拷贝的深度有些无意义,因为它没有嵌套属性,而我们通常在修改嵌套属性的背景下谈论深拷贝。

对于浅拷贝,只复制顶层属性,而不复制嵌套对象的值。因此:

  • 重新赋值拷贝的顶层属性不会影响源对象。
  • 重新赋值拷贝的嵌套对象属性会影响源对象。

在 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"]}]

另见