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 文章可以为您指明正确的方向。