JavaScript中的堆与栈、浅拷贝与深拷贝

栈(stack)和堆(heap)

堆栈是一个在计算机科学中经常使用的抽象数据类型。堆栈中的物体具有一个特性: 最后一个放入堆栈中的物体总是被最先拿出来, 这个特性通常称为后进先出(LIFO)队列。 堆栈中定义了一些操作。 两个最重要的是PUSH和POP。 PUSH操作在堆栈的顶部加入一 个元素。POP操作相反, 在堆栈顶部移去一个元素, 并将堆栈的大小减一。

堆(数据结构):堆可以被看成是一棵树,如:堆排序。

栈(数据结构):一种后进先出的的数据结构。

计算机的内存管理机制,也借鉴了堆栈这种数据结构来进行管理。

基本类型和引用类型

基本类型

存放在栈内存中的简单数据段,数据大小确定,内存空间大小可以分配。js中5种基本数据类型有Undefined、Null、Boolean、Number 和 String,它们是直接按值存放的,所以可以直接访问。

基本类型是不可更改的:任何方法都无法更改,对数字和布尔值来说显然如此,而对字符串来说就不那么明显了,因为字符串看起来像是由字符组成的数组,我们期望可以通过指定索引来修改字符串中的字符。实际上,js是禁止这样做的。字符串中所有的方法看上去像是返回了一个修改后的字符串,实际上返回的是一个新的字符串值。例如:

基本类型的比较是值的比较:只有在它们的值相等时它们才相等。如果比较两个单独的字符串,当且仅当它们的长度相等且每个索引的字符都相等时,js才认为它们相等。

引用类型

我们通常将对象称为引用类型,存放在堆内存中,变量实际保存的是一个指针(地址),这个指针指向另一个位置。每个空间大小不一样,要根据情况开进行特定的分配。当我们需要访问引用类型(如对象,数组,函数等)的值时,首先从栈中获得该对象的地址指针,然后再从堆内存中取得所需的数据。

引用类型的值是可以修改的:

对象的比较并非值的比较:即使两个对象包含同样的属性及相同的值,它们也是不相等的。各个索引元素完全相等的两个数组也不相等。

对象的值都是引用,对象的比较均是引用的比较:当且仅当它们的引用指向同一个基对象时,它们才相等。

思考题

结合上面所讲,思考一下为什么改变的时候,也被改变了?而改变的时候,并没有被改变?

浅拷贝和深拷贝

根据上面的陈述,基本类型拷贝的时候只是在内存中又开辟了新的空间,和它的父元素(再次我们称被拷贝的对象为父元素)属于 互不相干的东西,因此深浅拷贝是相对于引用类型的,以便于我们对引用类型父对象的保存。

浅拷贝

前面已经提到,在定义一个对象或数组时,变量存放的往往只是一个地址。当我们使用对象拷贝时,如果属性是对象或数组时,这时候我们传递的也只是一个地址。因此子对象在访问该属性时,会根据地址回溯到父对象指向的堆内存中,即父子对象发生了关联,两者的属性值会指向同一内存空间。

复制一份

如上代码,将对象(或数组)赋值给一个变量,仅仅是赋值的引用值:对象本身并没有复制移除。如果你想得到一个对象或数组的副本,则必须显示复制对象的每个属性或数组的每个元素。

不完全的深拷贝

由上面可分析:浅拷贝的时候,当我们改变子对象的数组的时候,父对象竟然也跟着改变,也就是说:子对象和父对象在浅拷贝的时候他们指向同一个内存的数组。

深拷贝

或许以上并不是我们在实际编码中想要的结果,我们不希望父子对象之间产生关联,那么这时候可以用到深拷贝。既然属性值类型是数组和或象时只会传址,那么我们就用递归来解决这个问题,把父对象中所有属于对象的属性类型都遍历赋给子对象即可。

方法一:

方法二:

方法三:

方法四:

方法五:

总结

理解深拷贝的递归你就能理解全部的拷贝精髓了。

分享 IT 技术和行业经验,请关注技术学派。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180603G0XUNX00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励