JavaScript 对象的复制并非表面看起来那么简单。理解此过程中对象和引用的工作方式对于 Web 开发人员至关重要,可以节省数小时的调试时间。当您使用大型有状态应用程序(例如在 React 或 Vue 中构建的应用程序)时,这一点变得越来越重要。
浅复制和深复制指的是我们在 JavaScript 中如何创建对象的副本以及在“副本”中创建了哪些数据。在本文中,我们将深入探讨这些方法之间的区别,探讨它们的实际应用,并揭示使用它们时可能出现的潜在陷阱。
浅复制是指创建新对象的过程,该对象是现有对象的副本,其属性引用与原始对象相同的数值或对象。在 JavaScript 中,这通常是使用 Object.assign()
或展开语法({...originalObject}
)等方法实现的。浅复制仅创建对现有对象或值的新的引用,不会创建深复制,这意味着嵌套对象仍然是引用的,而不是重复的。
让我们看看下面的代码示例。新创建的对象 shallowCopyZoo
是通过展开运算符创建的 zoo
的副本,这导致了一些意外的后果。
let zoo = { name: "Amazing Zoo", location: "Melbourne, Australia", animals: [ { species: "Lion", favoriteTreat: "?", }, { species: "Panda", favoriteTreat: "?", }, ], }; let shallowCopyZoo = { ...zoo }; shallowCopyZoo.animals[0].favoriteTreat = "?"; console.log(zoo.animals[0].favoriteTreat); // "?",而不是 "?"
但是让我们看看 shallowCopyZoo
中究竟是什么。属性 name
和 location
是原始值(字符串),因此它们的数值被复制。但是,animals
属性是对象的数组,因此复制的是对该数组的引用,而不是数组本身。
您可以使用严格相等运算符 (===
) 快速测试这一点(如果您不相信我)。只有当对象引用同一对象时,一个对象才等于另一个对象(参见原始数据类型与引用数据类型)。请注意,animals
属性在两者上是相等的,但对象本身并不相等。
let zoo = { name: "Amazing Zoo", location: "Melbourne, Australia", animals: [ { species: "Lion", favoriteTreat: "?", }, { species: "Panda", favoriteTreat: "?", }, ], }; let shallowCopyZoo = { ...zoo }; shallowCopyZoo.animals[0].favoriteTreat = "?"; console.log(zoo.animals[0].favoriteTreat); // "?",而不是 "?"
这可能导致代码库中出现潜在问题,并且在处理大型修改时尤其困难。修改浅副本中的嵌套对象也会影响原始对象和任何其他浅副本,因为它们都共享相同的引用。
深复制是一种创建新对象的技巧,该对象是现有对象的精确副本。这包括复制其所有属性和任何嵌套对象,而不是引用。当您需要两个不共享引用的独立对象时,深克隆很有用,确保对一个对象的更改不会影响另一个对象。
程序员在处理复杂应用程序中的应用程序状态对象时经常使用深克隆。创建新的状态对象而不影响先前状态对于维护应用程序的稳定性和正确实现撤消/重做功能至关重要。
JSON.stringify()
和 JSON.parse()
进行深复制一种流行且无需库的深复制方法是使用内置的 JSON.stringify()
和 JSON.parse()
方法。
parse(stringify())
方法并不完美。例如,Date
等特殊数据类型将被转换为字符串,未定义的值将被忽略。与本文中的所有选项一样,应根据您的具体用例进行考虑。
在下面的代码中,我们将使用这些方法创建一个 deepCopy
函数来深度克隆一个对象。然后,我们复制 playerProfile
对象并修改复制的对象,而不会影响原始对象。这展示了深复制在维护不共享引用的独立对象方面的价值。
console.log(zoo.animals === shallowCopyZoo.animals) // true console.log(zoo === shallowCopyZoo) // false
还有一些各种第三方库提供了深复制解决方案。
cloneDeep()
函数可以正确处理循环引用、函数和特殊对象。extend()
[deep = true]
函数如果由于某种原因您不想使用 JSON 对象或第三方库,您也可以在 Vanilla JavaScript 中创建一个自定义深复制函数。该函数递归迭代对象属性并创建一个具有相同属性和值的新对象。
const playerProfile = { name: 'Alice', level: 10, achievements: [ { title: 'Fast Learner', emoji: '?' }, { title: 'Treasure Hunter', emoji: '?' } ] }; function deepCopy(obj) { return JSON.parse(JSON.stringify(obj)); } const clonedProfile = deepCopy(playerProfile); console.log(clonedProfile); // 输出与playerProfile相同 // 修改克隆的配置文件而不影响原始配置文件 clonedProfile.achievements.push({ title: 'Marathon Runner', emoji: '?' }); console.log(playerProfile.achievements.length); // 输出:2 console.log(clonedProfile.achievements.length); // 输出:3
虽然深复制为数据准确性提供了极大的好处,但建议评估对于每个特定用例是否需要深复制。在某些情况下,浅复制或其他管理对象引用的技术可能更合适,从而提供更好的性能和降低复杂性。
Date
、RegExp
、DOM 元素)。例如,当深复制包含函数的对象时,可能会复制函数的引用,但不会复制函数的闭包及其绑定的上下文。同样,具有特殊特征的对象在深复制时可能会丢失其独特的属性和行为。JSON.parse(JSON.stringify(obj))
这样的内置方法也有一些限制,例如无法正确处理函数、循环引用或特殊对象。虽然有一些第三方库(如 Lodash 的 _.cloneDeep()
)可以更有效地处理深复制,但为深复制添加外部依赖项可能并不总是理想的。感谢您抽出时间阅读本文。浅复制与深复制比任何初学者想象的都要复杂得多。尽管每种方法都有很多陷阱,但花时间审查和考虑这些选项将确保您的应用程序和数据保持您想要的样子。
浅复制和深复制之间的主要区别在于它们处理作为对象的属性的方式。在浅复制中,复制的对象与原始对象共享对嵌套对象的相同引用。这意味着对嵌套对象的更改将反映在原始对象和复制对象中。另一方面,深复制会创建嵌套对象的新实例,这意味着对复制对象中嵌套对象的更改不会影响原始对象。
JavaScript 中的展开运算符(…)通常用于浅复制。它将一个对象的所有可枚举自身属性复制到另一个对象。但是,它只复制第一级的属性和嵌套对象的引用。因此,对嵌套对象的更改将影响原始对象和复制的对象。
是的,您可以使用 JSON 方法在 JavaScript 中进行深复制。JSON.stringify()
和 JSON.parse()
方法的组合可以创建对象的深复制。JSON.stringify()
将对象转换为字符串,JSON.parse()
将字符串解析回新对象。但是,此方法有一些限制,因为它不会复制方法,并且不适用于 Date
、RegExp
、Map
、Set
等特殊 JavaScript 对象。
浅复制只复制第一级的属性和嵌套对象的引用。因此,如果原始对象包含嵌套对象,则对这些嵌套对象的更改将影响原始对象和复制的对象。这可能导致代码中出现意外的结果和错误。
Object.assign()
方法如何在浅复制中工作?Object.assign()
方法用于将一个或多个源对象的所有可枚举自身属性的值复制到目标对象。它返回目标对象。但是,它执行浅复制,这意味着它只复制第一级的属性和嵌套对象的引用。
在 JavaScript 中深复制对象的最佳方法取决于代码的具体要求。如果您的对象不包含方法或特殊的 JavaScript 对象,您可以使用 JSON.stringify()
和 JSON.parse()
方法的组合。对于更复杂的对象,您可能需要使用 Lodash 等库,该库提供深克隆函数。
不可以,JavaScript 中的展开运算符只执行浅复制。它复制第一级的属性和嵌套对象的引用。要执行深复制,您需要使用其他方法或库。
深复制可能比浅复制更消耗资源,尤其对于大型对象而言。这是因为深复制为所有嵌套对象创建新实例,这可能会占用更多内存和处理能力。
JSON.stringify()
和 JSON.parse()
等深复制方法不处理循环引用,并将抛出错误。循环引用发生在一个对象的属性引用该对象本身时。要处理循环引用,您需要使用支持它的库,例如 Lodash。
了解浅复制和深复制之间的区别对于管理 JavaScript 中的数据至关重要。它会影响您的对象如何相互交互。如果您不小心,浅复制会导致意外的结果和错误,因为对嵌套对象的更改会影响原始对象和复制的对象。另一方面,深复制确保您的复制对象与原始对象完全独立。
以上是浅与深层复制的JavaScript的详细内容。更多信息请关注PHP中文网其他相关文章!