了解JavaScript中Shallow和Deep copies的情况是很重要的。为了理解这一点,我们将看一下内存分配和它在JavaScript中的行为。
我们今天的目标是回答这个问题:为什么我们要关注这些概念和它们的区别?
如果你刚刚发现了浅层拷贝,或者正在寻求澄清,那么我推荐你把《在JavaScript中浅层克隆对象的3种方法》作为书签。作者的红包很有意思!我喜欢在阅读技术文档时有一个准备好的JavaScript副本并排运行。当务之急是让你所选择的节点环境运行起来,这样你就可以在摸着石头过河的时候进行练习。
使用GitHub或Bit这样的东西来跟踪你的文件也是明智的。这样你就可以云端保存你更常用的、抽象的和重构的代码块。然后从复制笔记/练习中取出你的工作片段,在GitHub或Bit可以跟踪的强大空间内创建重构的功能。一旦你得到了一个功能或解决方案,干净,可扩展;那么,它很可能在你的工作中被用于其他地方--所以为什么不把它变成一个绿色的广场?我的目标是通过这个通用的、可改变的程序来满足用可重复使用的代码进行模块化建设的最佳实践,此外,在实施解决方案时,尽可能地了解引擎盖下的操作。
帮助我创建自己的例子和解释的进一步阅读(如下)可以在MDN的JS内存管理中找到。
我已经分享了让我有点兴趣的来源。现在我想告诉你,在我消化了一些材料之后,我喜欢如何玩弄代码。如果你和这篇文章一起工作的话,所包含的进一步阅读会很轻松。
下面是一个基本的拷贝,为新的拷贝创建了一个内存空间--由于它们的原始值/数据类型,它们都是不可改变的(当拷贝时)。换句话说:它们是按值分配/传递的。
//Primitive value copy
let x = 400
x = "This string"
console.log(y) //400
当y被赋值时,它复制了x的值(因为它在运行时--x被重新赋值之前)。这里的重要启示是,你可以通过创建和分配另一个变量给被复制的变量,在单独的内存空间中快速复制一个原始数据类型的精确值。请注意它的实例化方式--const不允许后来的改变。

内存分配和基元值分配的视觉进展
//We can go a little further with the above block and re-assign x to the original value; however, we know they are separate memory spaces with same values
x = y
主要焦点
我们必须处理更复杂的数据类型和结构:引用值--如对象和数组(这是JS对象的一种特殊类型)。一个对象有可访问的属性,又称 "键"。这些键有自己的值,可供修改。JS对象(作为非原始数据类型)不同,因为它们有参考值,而且这些值是可变的。这意味着它们在浅层复制时共享一个内存地址。
注意:下面的例子是一个浅层对象-字面被浅层复制--意味着它们的值将是相同的,因为它们都是对同一内存空间的引用。在JS中,"浅层对象 "是一个非嵌套的、非原始的JS数据类型。
//Shallow Clone of a Shallow Object
console.log(shallowObj) // {key1: 5, key2: 2}
注意,在shallowObj的key1处重新赋值也会改变newObj的key1值。它们在内存中共享一个空间,并且有独立的变量名在shallowObj.key1被改变的情况下,我们可以改为更新newObj.key1,我们得到同样的结果。
这在某些情况下是很有用的,我们想表示一个特定对象的一堆相同的属性和值;然而,在以后的运行时间里,这些浅层克隆对象可能需要不同的变量名来配合你的应用程序的目标--作为不同的道具传递给其他组件以达到不同的目的。
我很想听到更多关于这个概念在现实世界的应用。值得一提的是,理解这一点有助于以后的工作。
在继续之前,我想强调深拷贝与浅拷贝有这样的区别:深拷贝是为原变量的部分或全部(取决于数据的复杂性)的值分配一个新的内存地址。深度拷贝更进一步,将元素' 价值*.*
浅层对象的深度拷贝
//
let myRadio = { podcasts: 19,
deepCopyMyRadio.playlists = 62 // only changes deepCopyMyRadio
console.log(deepCopyMyRadio) // => { podcasts: 19,
ES6用传播操作符引入了一点语法上的糖。传播操作符由三个连续的点"... "表示,在代码的几个不同部分被使用。一般来说,传播操作符是为给定对象的每个 (顶层)属性制作一个副本,然后将它们传播到一个新对象。
这取决于我们决定什么时候我们需要一个特定嵌套对象的浅层或深层拷贝。现在我们展示的是一个浅层对象的深层拷贝,所以Object.assign()方法或下面的例子就可以了。
//Deep Copy with spread operator
let myRadio = { podcasts: 9,
myRadio.albums = 88 //again only changes myRadio
console.log myRadio //=> { podcasts: 9, albums: 88, playlists: 4 }
麻烦的是,随着项目中规模和复杂性的增加,传播操作符是一个有限的解决方案。传播操作符可以处理浅层对象的深层拷贝(非嵌套--上文)。对于轻量级的代码块,传播操作符表现得很好。

在这种情况下,拷贝被这样分配:someOtherVar = someVar
下面我们看到传播操作符不会像我们预期的那样处理嵌套对象的复杂性。
对于嵌套对象,传播操作符将提供一个深度拷贝给数值的第一个实例,但将所有的嵌套数据作为浅层拷贝与原始数据共享一个内存空间。请注意这种行为!

population.total在city和shallowCity中都共享一个内存空间。spread操作符抓取top lair数据并将其添加到单独的内存空间;因此,shallowCity的名称属性实际上已经改变。
提醒:我们,向导,决定哪一个对我们要解决的问题/程序的内存使用是最好的(浅层或深层拷贝)。
我们是否需要单独传递引用,还是也要传递它们的值?是否有必要为一个变量创建一块新的内存,或者它可以共享原始对象的值?
我想到了深度拷贝的一个具体用途:创建一个深度嵌套的对象来作为模板。模板将需要和任何嵌套的默认值一起被复制到一个单独的内存空间。
这样我们就可以对拷贝进行程序化的修改,并为自己在整个项目中动态使用一个对象做准备。
最后,我们使用ES6中现成的工具对一个深层对象进行深度拷贝,这是一种非常流行的方法。在这里还可以找到其他值得了解的方法。
深度复制深度数据的解决方案。JSON.parse(JSON.stringify())
这里我们有一个我的家庭游戏库的例子。从前,我能够玩一堆大男孩的视频游戏 . .

第78行是我们使用ES6在内存中为adultGames做一个新的位置,其新值被修改了第80-82行
在vanilla JavaScript中复制对象/数组的缺点是有嵌套值。正如你所看到的:钻进去,虽然足够容易,但也会很乏味。
我真的很喜欢这些执行的ES6传播操作者的其他很酷的用法如果你想知道我前面说的传播操作符在代码的不同部分使用是什么意思,你一定要看看。
这篇文章非常透彻地分析了用其他内置的ES6方法复制数组和对象的可能用途!
这里介绍的所有信息都是我的最佳理解。欢迎在评论中提出任何更正或反馈。
在你所有的项目中使用任何组件
到现在为止,你习惯于在更大的项目中隐藏构建功能。
但是,如果你先开发独立的功能,然后在许多应用程序中轻松地编排和管理它们,会怎么样呢?你的开发将变得更快,更一致,而且每天都有可扩展性。一旦创建一个组件,就可以真正在任何地方使用它来构建任何东西。
像Bit这样的开放源码软件工具为构建独立的组件并将其组合到应用程序中提供了强大的开发者体验。你可以从一个不错的项目、一些共享组件、甚至是尝试微前端开始,从小做起。试一试吧→
一个独立的产品组件,可以在任何地方使用。
Shallow Copy vs Deep Copy in JavaScript最初发表于Bits and Pieceson Medium,人们在这里通过强调和回应这个故事来继续对话。