在 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]
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Web 2.0 Architectures
Duane Nickull、Dion Hinchcliffe、James Governor / O'Reilly / 2009 / USD 34.99
The "Web 2.0" phenomena has become more pervasive than ever before. It is impacting the very fabric of our society and presents opportunities for those with knowledge. The individuals who understand t......一起来看看 《Web 2.0 Architectures》 这本书的介绍吧!