在 JavaScript 中对象的深拷贝(及其工作原理)[每日前端夜话0x8F]
栏目: JavaScript · 发布时间: 5年前
内容简介:作者:Chris Chu翻译:疯狂的技术宅来源:
每日前端夜话 0x8F
每日前端夜话,陪你聊前端。
每天晚上18:00准时推送。
正文共:1300 字
预计阅读时间:6 分钟
作者:Chris Chu
翻译:疯狂的技术宅
来源: alligator
如果你打算用 JavaScript 进行编码,那么就需要了解对象的工作方式。对象是 JavaScript 最重要的元素之一,深入理解了它会使你在编码时得心应手。在克隆对象时,它并不像看起来那么简单。
当你不想改变原始对象时,就需要克隆对象。例如,如果你有一个接受对象并改变它的函数,可能不想改变其原始对象。
那么让我们在 JavaScript 中创建一个对象:
1let testObject = { 2 a: 1, 3 b: 2, 4 c: 3 5};
在上面的代码片段中,我们初始化一个新对象并将其分配给变量 testObject
。现在对于大多数初学者来说,他们会试着通过将 testObject
分配给新变量来创建这个对象的副本,以便在其代码中进行操作。很抱歉用这种方法行不通。
下面是一个代码片段,说明了为什么不起作用。
1let testObject = { 2 a: 1, 3 b: 2, 4 c: 3 5}; 6 7// 为 testObject 创建一个副本 8let testObjectCopy = testObject; 9 10testObject.a = 9; 11console.log(testObjectCopy.a); 12// 这里 a = 9
如上面的代码片段所示,创建新变量 testObjectCopy
实际上并不创建 testObject
的副本。相反它只是引用 testObject
。你对所谓的副本做的任何更改也将反映在原始对象中。
循环遍历对象并将每个属性复制到新对象也不起作用。
1const copyObject = object => { 2 // 这是存储原始对象属性的对象 3 let copiedObj = {}; 4 5 for (let key in object) { 6 // 这里将每个属性从原始对象复制到复制对象 7 copiedObj[key] = object[key]; 8 } 9 10 return copiedObj; 11}; 12 13const testObject = { 14 a: 5, 15 b: 6, 16 c: { 17 d: 4 18 } 19}; 20 21copyObject(testObject);
上述方法存在以下几个问题:
-
1. 将每个属性复制到新对象的循环只会复制对象上的可枚举属性。可枚举属性是将要出现在
for
循环和Object.keys
中的属性。 -
2. 复制的对象有一个新的
Object.prototype
方法,这不是复制对象时所需的方法。 -
3. 如果对象具有作为对象的属性,则复制的对象实际上将会引用原始对象而不是创建副本。这意味着如果更改复制对象中的嵌套对象,原始对象也会更改。
-
4. 不复制任何属性描述符。如果将
configurable
或writable
设置为false
,则复制对象中的属性描述符将会默认为true
。
那么应该怎样正确的复制对象?
对于仅存储基本类型(如数字和字符串)的简单对象,上述浅层复制方法将起作用。但是如果对象具有对其他嵌套对象的引用,则不会复制实际对象。你只会复制对其的 引用 。
对于深层复制,最简单的选择是使用可靠的外部库,如 Lodash 。
使用 Lodash 的 Clone 和 Clonedeep
Lodash 提供两种不同的功能,允许你进行浅拷贝和深拷贝,它们是 clone
和 clonedeep
。Lodash 的优点在于你可以单独导入它的每个函数,而无需将整个库放入你的项目中。这可以大大的减少依赖项的大小。
1const clone = require('lodash/clone'); 2const cloneDeep = require('lodash/clonedeep'); 3 4// 你也可以这样做: 5// const clone = require('lodash.clone'); 6// const cloneDeep = require('lodash.clonedeep'); 7// 取决于你自己的风格 :)
现在就用 clone
和 clonedeep
函数做一些尝试:
1const clone = require('lodash/clone'); 2const cloneDeep = require('lodash/clonedeep'); 3 4const externalObject = { 5 animal: 'Gator' 6}; 7 8const originalObject = { 9 a: 1, 10 b: 'string', 11 c: false, 12 d: externalObject 13}; 14 15const shallowClonedObject = clone(originalObject); 16 17externalObject.animal = 'Crocodile'; 18 19console.log(originalObject); 20console.log(shallowClonedObject); 21// originalObject 和 shallowClonedObject 中的`animal`属性 22// 是同时被改变的,因为它是一个浅的副本。 23 24const deepClonedObject = clonedeep(originalObject); 25 26externalObject.animal = 'Lizard'; 27 28console.log(originalObject); 29console.log(deepClonedObject); 30// 原始对象中的'animal'属性发生了变化,但对于 31// deepClonedObject,它复制后仍然是'Crocodile' 32// 对象是独立的而不是复制引用。
在上面的代码中,我们创建了一个名为 originalObject
的对象,它存储了 7 个属性,每个属性都有不同的值。属性 d
引用我们的 externalObject
,它具有值为 Gator
的 animal
的属性。
当从 Lodash 执行 clone
函数时,它会创建一个对象的浅层副本,我们将其分配给 shallowClonedObject
。在 externalObject
中为 animal
属性赋值一个新值将改变 originalObject
和 shallowClonedObject
,因为浅拷贝只能将引用复制到 externalObject
并它没有为自己创造一个全新的对象。
这就是 clonedeep
函数的用武之地。如果你对 deepClonedObject
执行相同的处理,那么 originalObject
的 d
属性是唯一要改变的属性。
试一试,看看它如何帮助你编码!:rocket:
原文: https://alligator.io/js/deep-cloning-javascript-objects/
下面夹杂一些私货:也许你和高薪之间只差这一张图
2019年京程一灯课程体系上新,这是我们第一次将全部课程列表对外开放。
愿你有个好前程,愿你月薪30K。我们是认真的 !
在公众号内回复“体系”查看高清大图
长按二维码,加大鹏老师微信好友
拉你加入前端技术交流群
唠一唠怎样才能拿高薪
往期精选
小手一抖,资料全有。长按二维码关注 前端先锋 ,阅读更多技术文章和业界动态。
以上所述就是小编给大家介绍的《在 JavaScript 中对象的深拷贝(及其工作原理)[每日前端夜话0x8F]》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- Vue生命周期钩子简介[每日前端夜话0x8A]
- Node.js 究竟是什么?[每日前端夜话0x72]
- 关于 Git 的 20 个面试题[每日前端夜话0x75]
- React 的未来,与 Suspense 同行[每日前端夜话0x85]
- 一文学会Vue中间件管道[每日前端夜话0x8C]
- 为什么要用 Node.js?[每日前端夜话0x77]
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。