Spread Syntax 会创建浅拷贝还是深拷贝?

esp*_*spc 4 javascript deep-copy shallow-copy

几天来我对浅拷贝和深拷贝的真正定义感到非常困惑。

\n

当我阅读浅拷贝的 mdn 文档(https://developer.mozilla.org/en-US/docs/Glossary/Shallow_copy)时,这一切都有意义。第一段清楚地解释了什么是浅拷贝。文档说

\n
\n

对象的浅拷贝是其属性与从中创建副本的源对象共享相同引用(指向相同基础值)的副本。因此,当您更改源或副本时,也可能导致其他对象也发生更改\xe2\x80\x94,因此,您最终可能会无意中导致对源或副本进行您不希望的更改\'预计。

\n
\n

我完全明白了那部分。根据我的理解,下面的代码示例是浅复制的示例,因为更改源或副本也会导致另一个对象发生更改。

\n
let a = {\n    food: "pasta",\n    restaurantName: "myPastPlace"\n}\n\nlet b = a\n\nb.food = "hamburger"\n\nconsole.log(b.food) //hamburger\nconsole.log(a.food) //hamburger\n
Run Code Online (Sandbox Code Playgroud)\n

所以,令人困惑的部分是当我使用扩展语法来制作副本时。这是深拷贝还是浅拷贝?因为对于我来说,对于第一级深度来说,扩展语法(运算符)正在制作深度复制。然而,MDN 文档表示扩展语法创建的是浅拷贝而不是深拷贝。

\n
\n

在 JavaScript 中,所有标准内置对象复制操作(扩展语法、Array.prototype.concat()、Array.prototype.slice()、Array.from()、Object.assign() 和 Object.create() )创建浅拷贝而不是深拷贝。

\n
\n
let a = {\n    food: "pasta",\n    restaurantName: "myPastPlace"\n}\n\nlet b = {...a}\nconsole.log(b)\n\nb.food = "hamburger"\n\nconsole.log(b.food) //hamburger\nconsole.log(a.food) //pasta\n\n
Run Code Online (Sandbox Code Playgroud)\n

Ama*_*dan 7

变量可以包含一个值(在原始值的情况下,如1)或引用(在对象的情况下,如 ){ food: "pasta" }。原始类型只能被复制,并且由于它们不包含属性,因此不存在浅/深区别。

\n

如果您将引用本身视为原始值,则 是引用b = a的副本。但由于 JavaScript 不允许您直接访问引用(就像 C 那样,其中等效的概念是指针),因此将复制引用称为“复制”会产生误导和混淆。在 JavaScript 上下文中,“复制”是值的副本,引用不被视为值。

\n

对于非原始值,存在三种不同的情况:

\n
    \n
  • 由 所创建的共引用b = a仅指向同一个对象;如果您a以任何方式进行修改,b都会受到同样的影响。直觉上你会说无论你修改哪个对象都会反映在副本中,但这是错误的:没有副本,只有一个对象。无论您从哪个引用访问对象,它都是同一个实体。这就像打皮特先生一巴掌,然后想知道为什么布拉德对你生气\xe2\x80\x94 布拉德和皮特先生是同一个人,而不是克隆人。
  • \n
\n

\r\n
\r\n
let a = {\n    food: "pasta",\n    contents: {\n        flour: 1,\n        water: 1\n    }\n}\nlet b = a;\na.taste = "good";\na.contents.flour = 2;\nconsole.log(b);
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n

    \n
  • 浅拷贝创建对象的副本,但包含引用的任何属性仍保持 \xe2\x80\x94 状态,从而导致共引用属性。如果更改其中一个对象,另一个对象不会受到影响;但是,如果您更改属性引用的任何对象,您也会看到其他对象的更改。在此示例中,您将看到b.contents.flour受到 的更改的影响a(因为b.contentsa.contents引用同一个对象{ flour: 1, water: 2 }),但a.taste它不存在(因为ab本身就是对象)。
  • \n
\n

\r\n
\r\n
let a = {\n    food: "pasta",\n    contents: {\n        flour: 1,\n        water: 1\n    }\n}\nlet b = {...a};\na.taste = "good";\na.contents.flour = 2;\nconsole.log(b);
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n

    \n
  • 深复制也会递归地复制每个属性,以便不存在共引用 \xe2\x80\x94 无论原始对象及其属性发生什么,复制都不会受到影响。在这里, 和b.tasteb.contents.flour不受 变化的影响a
  • \n
\n

\r\n
\r\n
let a = {\n    food: "pasta",\n    contents: {\n        flour: 1,\n        water: 1\n    }\n}\nlet b = structuredClone(a);\na.taste = "good";\na.contents.flour = 2;\nconsole.log(b);
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n

请注意,此时structuredClone还很新;如果任何用户使用旧版浏览器,您可能需要使用 polyfill。

\n