美文网首页让前端飞Web前端之路程序园
在 JavaScript 中对象的深拷贝(及其工作原理)

在 JavaScript 中对象的深拷贝(及其工作原理)

作者: 编程小世界 | 来源:发表于2019-07-05 22:36 被阅读3次

    如果你打算用 JavaScript 进行编码,那么就需要了解对象的工作方式。对象是 JavaScript 最重要的元素之一,深入理解了它会使你在编码时得心应手。在克隆对象时,它并不像看起来那么简单。

    当你不想改变原始对象时,就需要克隆对象。例如,如果你有一个接受对象并改变它的函数,可能不想改变其原始对象。

    那么让我们在 JavaScript 中创建一个对象:

    1let testObject = {2a:1,3b:2,4c:35};

    在上面的代码片段中,我们初始化一个新对象并将其分配给变量 testObject 。现在对于大多数初学者来说,他们会试着通过将 testObject 分配给新变量来创建这个对象的副本,以便在其代码中进行操作。很抱歉用这种方法行不通。

    下面是一个代码片段,说明了为什么不起作用。

    1lettestObject = { 2  a: 1, 3  b: 2, 4  c: 3 5}; 6 7// 为testObject 创建一个副本 8lettestObjectCopy =testObject; 910testObject.a = 9;11console.log(testObjectCopy.a);12// 这里 a = 9

    如上面的代码片段所示,创建新变量 testObjectCopy 实际上并不创建 testObject 的副本。相反它只是引用 testObject 。你对所谓的副本做的任何更改也将反映在原始对象中。

    循环遍历对象并将每个属性复制到新对象也不起作用。

    1constcopyObject =object=> {2// 这是存储原始对象属性的对象3letcopiedObj = {};45for(letkeyinobject) {6// 这里将每个属性从原始对象复制到复制对象7copiedObj[key] =object[key];8}910returncopiedObj;11};1213consttestObject = {14a:5,15b:6,16c: {17d:418}19};2021copyObject(testObject);

    上述方法存在以下几个问题:

    1. 将每个属性复制到新对象的循环只会复制对象上的可枚举属性。可枚举属性是将要出现在 for 循环和 Object.keys 中的属性。

    2. 复制的对象有一个新的 Object.prototype 方法,这不是复制对象时所需的方法。

    3. 如果对象具有作为对象的属性,则复制的对象实际上将会引用原始对象而不是创建副本。这意味着如果更改复制对象中的嵌套对象,原始对象也会更改。

    4. 不复制任何属性描述符。如果将 configurable 或 writable 设置为 false ,则复制对象中的属性描述符将会默认为 true 。

    那么应该怎样正确的复制对象?

    对于仅存储基本类型(如数字和字符串)的简单对象,上述浅层复制方法将起作用。但是如果对象具有对其他嵌套对象的引用,则不会复制实际对象。你只会复制对其的 引用 。

    对于深层复制,最简单的选择是使用可靠的外部库,如 Lodash 。

    使用 Lodash 的 Clone 和 Clonedeep

    Lodash 提供两种不同的功能,允许你进行浅拷贝和深拷贝,它们是 clone 和 clonedeep 。Lodash 的优点在于你可以单独导入它的每个函数,而无需将整个库放入你的项目中。这可以大大的减少依赖项的大小。

    1constclone=require('lodash/clone');2constcloneDeep =require('lodash/clonedeep');34// 你也可以这样做:5// const clone = require('lodash.clone');6// const cloneDeep = require('lodash.clonedeep');7// 取决于你自己的风格 :)

    现在就用 clone 和 clonedeep 函数做一些尝试:

    1constclone =require('lodash/clone');2constcloneDeep =require('lodash/clonedeep');34constexternalObject = {5animal:'Gator'6};78constoriginalObject = {9a:1,10b:'string',11c:false,12d: externalObject13};1415constshallowClonedObject = clone(originalObject);1617externalObject.animal ='Crocodile';1819console.log(originalObject);20console.log(shallowClonedObject);21// originalObject 和 shallowClonedObject 中的`animal`属性22// 是同时被改变的,因为它是一个浅的副本。2324constdeepClonedObject = clonedeep(originalObject);2526externalObject.animal ='Lizard';2728console.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 属性是唯一要改变的属性。

    试一试,看看它如何帮助你编码!

    如果有想学习编程的初学者,可来我们的前端直播授课群的哦:571671034里面免费送整套系统的前端教程!

    相关文章

      网友评论

        本文标题:在 JavaScript 中对象的深拷贝(及其工作原理)

        本文链接:https://www.haomeiwen.com/subject/zkgyhctx.html