JavaScript中深度克隆对象的工作原理
介绍
如果您打算使用 JavaScript 进行编码,则需要了解对象的工作原理。 对象是 JavaScript 中最重要的元素之一,深入了解对象总是有用的。
了解如何在 JavaScript 中正确克隆对象很重要。 可以创建对象的浅拷贝和深拷贝。 对象的浅拷贝引用原始对象。 因此,对原始对象所做的任何更改都将反映在副本中。 深层副本是原始对象的所有元素的副本。 对原始对象所做的更改不会反映在副本中。 在本文中,您将使用 Lodash 库 创建对象的深层副本。
先决条件
要完成本教程,您将需要以下内容:
- 您的机器上安装了最新版本的 Node。 要安装 Node,请按照 如何安装 Node.js 教程中概述的步骤进行操作。
- 了解如何使用 npm 安装模块和包以及如何配置
package.json
文件。 这篇文章 How to Use Node.js Modules with npm and package.json 可以帮助你。 - 对 JavaScript 编码的基本了解,您可以在名为 如何在 JavaScript 中编码 的系列中找到它
第 1 步 – 通过重新分配对象创建浅拷贝
如果您创建一个接收对象并更改它的函数,您可能想要创建对象的副本并更改副本而不是改变原始对象。
初始化一个新对象并将其分配给变量 testObject
。 此对象应具有字母 a
、b
和 c
作为键和 1
、2
和 3
作为值,分别。
在 JavaScript 中创建对象:
let testObject = { a: 1, b: 2, c: 3 };
现在,尝试通过将 testObject
分配给名为 testObjectCopy
的新变量来创建此对象的副本以进行操作:
let testObject = { a: 1, b: 2, c: 3 }; let testObjectCopy = testObject;
更改 testObject
中键 a
的值。 将其设置为 9
:
let testObject = { a: 1, b: 2, c: 3 }; let testObjectCopy = testObject; testObject.a = 9;
您可能希望此更改仅反映在 testObject
对象中。 使用 console.log
语句检查 a
在 testObjectCopy
中的值:
let testObject = { a: 1, b: 2, c: 3 }; let testObjectCopy = testObject; testObject.a = 9; console.log(testObjectCopy.a);
尽管引用了 testObjectCopy
而不是 testObject
,但此 console.log
语句将 9
打印到控制台。 这是因为创建新变量 testObjectCopy
不会创建 testObject
副本。 相反,它引用了 testObject
。 您对原始对象所做的任何更改都将反映在假定的副本中,反之亦然。
将对象重新分配给新变量只会创建原始对象的浅表副本。 在下一步中,您将探索循环对象如何成为创建深层副本的可能解决方案。
第 2 步 – 通过对象循环创建浅拷贝
遍历对象并将每个属性复制到新对象似乎是一个可行的解决方案。 要对此进行测试,请创建一个名为 copyObject
的函数,该函数接受一个名为 object
的参数:
const copyObject = object => { };
在 copyObject
中,声明一个名为 copiedObj
的变量,该变量将保存一个空对象:
const copyObject = object => { let copiedObj = {}; };
为 object
中的每个键创建一个 for
循环。 将 copiedObj
中的键/值对设置为等于 object
中的键/值对:
const copyObject = object => { let copiedObj = {}; for (let key in object) { copiedObj[key] = object[key]; } };
对于此 copyObject
函数的最后一步,返回 copiedObj
:
const copyObject = object => { let copiedObj = {}; for (let key in object) { copiedObj[key] = object[key]; } return copiedObj; };
在 copyObject
就位后,创建一个名为 testObject
的对象并将其作为 copyObject
的参数传入:
const copyObject = object => { let copiedObj = {}; for (let key in object) { copiedObj[key] = object[key]; } return copiedObj; }; const testObject = { a: 5, b: 6, c: { d: 4 } }; copyObject(testObject);
要查看 copyObject
函数的结果,请使用 console.log
查看打印到控制台的 copyObject(testObject)
的输出:
console.log(copyObject(testObject));
这将产生以下输出:
Output{ a: 5, b: 6, c: { d: 4 } }
似乎循环通过 testObject
创建副本产生了预期的结果。 但是有几个原因导致这种方法无法为您提供所需的结果:
- 将每个属性复制到新对象的循环只会复制对象上的可枚举属性。 Enumerable properties 是显示在
for
循环和Object.keys
中的属性。 - 复制的对象有一个新的
Object.prototype
方法,这不是您复制对象时想要的。 这意味着您对原始对象所做的任何更改都将反映在复制的对象中。 - 如果您的对象具有作为对象的属性,则您复制的对象实际上将引用原始对象,而不是创建实际副本。 这意味着如果您更改复制对象中的嵌套对象,原始对象也会更改。
- 不会复制任何属性描述符。 如果将
configurable
或writable
设置为false
,则复制对象中的属性描述符将默认为true
。
循环对象允许您创建浅拷贝,但无法使用此方法创建深拷贝。 值得庆幸的是,有一个库可以提供创建深层副本的解决方案。
第 3 步 – 使用 Lodash 创建浅拷贝和深拷贝
对于仅存储原始类型(如数字和字符串)的简单对象,上述浅拷贝方法将起作用。 但是,如果您的对象具有对其他嵌套对象的引用,则不会复制实际对象。 您只会复制参考。
对于深拷贝,一个不错的选择是使用可靠的外部库,例如 Lodash。 Lodash 是一个库,它提供了两种不同的功能,允许您进行浅拷贝和深拷贝。 它们是 clone
和 clonedeep
。
要测试 Lodash clone
和 clonedeep
功能,您需要先安装 Lodash:
npm install --save lodash
现在安装了 lodash,使用 require()
函数现在可以访问 Lodash 也提供的所有函数:
const _ = require('lodash');
现在您可以在代码中使用 clone
和 clonedeep
函数。 创建一个名为 externalObject
的对象。 给出一个值为 'Gator'
的键 animal
:
const externalObject = { animal: 'Gator' };
创建另一个名为 originalObject
的对象。 originalObject
将存储七个具有不同值的属性。 属性 d
引用了 externalObject
,它具有 animal
的属性和 'Gator'
的值。
const originalObject = { a: 1, b: 'string', c: false, d: externalObject };
使用 clone
创建浅拷贝
声明一个常量变量 shallowClonedObject
并使用 Lodash clone
函数将其分配给 originalObject
的浅表副本:
const shallowClonedObject = _.clone(originalObject);
在 externalObject
中重新分配 animal
键的值。 将其设置为 'Crocodile'
。 使用两个 console.log
语句将 originalObject
和 shallowClonedObject
都打印到屏幕上:
externalObject.animal = 'Crocodile'; console.log(originalObject); console.log(shallowClonedObject);
此代码的输出将如下所示:
Output{ a: 1, b: 'string', c: false, d: { animal: 'Crocodile' } } { a: 1, b: 'string', c: false, d: { animal: 'Crocodile' } }
将 externalObject
中的 animal
属性分配给新值将同时更改 originalObject
和 shallowClonedObject
。 console.log
语句将显示这一点。 发生这种情况是因为浅克隆只能复制对 externalObject
的引用,而不是创建一个全新的对象。
使用 clonedeep
创建深层副本
您可以使用 Lodash clonedeep
函数创建深层副本:
const deepClonedObject = _.clonedeep(originalObject);
在 deepClonedObject
就位后,将 externalObject
中的 animal
键的值重新分配为等于 'Lizard'
。
同样,使用两个 console.log
语句将 originalObject
和 deepClonedObject
打印到屏幕上:
externalObject.animal = 'Lizard'; console.log(originalObject); console.log(deepClonedObject);
此代码的输出将如下所示:
Output{ a: 1, b: 'string', c: false, d: { animal: 'Lizard' } } { a: 1, b: 'string', c: false, d: { animal: 'Crocodile' } }
originalObject
中的 'animal'
属性发生了变化,但对于 deepClonedObject
,它保持为 'Crocodile'
,因为整个对象是单独复制的,而不是复制引用。 使用 clonedeep
函数可以成功创建对象的深层副本。
结论
了解如何在 JavaScript 中深度克隆对象很重要。 您通过重新分配和循环对象来创建对象的浅表副本。 您还使用了 Lodash 库来创建对象的浅拷贝和深拷贝。
如果你想了解更多关于 JavaScript 中的对象,这个 Understanding Objects in JavaScript 教程是一个很好的起点。 如果您想更进一步并学习如何复制对象方法,Copying Objects in JavaScript 文章可以为您指明正确的方向。