deepthan/Front-end-resource-collection

白话JavaScript的原始类型、引用类型、栈、堆、浅拷贝、深拷贝的概念

deepthan opened this issue · 0 comments

JavaScript数据类型分为两类:原始类型(primitive type)和对象类型(object type)

  • 原始类型
    包括 数字、字符串、布尔值、null、undefined 。
    变量是直接按值存放的,存放在栈内存中的简单数据段,可以直接访问。
  • 对象(引用)类型
    包括数组、函数、对象。
    存放在堆内存中的对象,变量保存的是一个指针,这个指针指向另一个位置。当需要访问引用类型(如对象,数组等)的值时,首先从栈中获得该对象的地址指针,然后再从堆内存中取得所需的数据。

怎么解释呢?

^-^ 小例子

  • 用钱买东西:其实我们的口袋就像是栈,银行就像是堆,要买的物品就变量,要付的钱就是原始类型或引用类型。
  • 我们买一瓶水(变量)从口袋(栈)拿出钱(原始类型)付款,钱(原始类型)是在口袋(栈)的,付钱速度是非常快的;
  • 如果我们买车(变量)的话也得付钱,这个钱刷银行卡(引用类型),钱在银行(堆),银行卡(引用类型)相当于指针指向你存在银行的大么一大堆钱,不管你在银行存多少钱,你的银行卡大小是固定的。你如果直接带那么多钱(原始类型)放在口袋(栈),会撑爆的~ 所以得带银行卡(引用类型),钱放在银行(堆)。

为变量赋值时,JavaScript的解释程序必须判断该值是原始类型还是引用类型。解析程序则需尝试判断该值是否为JavaScript的原始类型之一。由于这些原始类型占据的空间是固定的,所以可将它们存储在较小的内存区域栈中。这样存储便于迅速查找变量的值。

如果一个值是引用类型,那么它的存储空间会从堆中分配,由于引用值的大小会改变,所以不能将它放在栈中,否则会降低变量查询速度。相反,放在变量的栈空间中的值是该对象存储在堆中的地址,地址的大小是固定的,所以把它存储在栈中对变量性能无任何负面影响。

JavaScript数据类型分为两类也可以分为可变类型(mutable)和不可变类型(immutable)

  • 可变类型(引用类型内容)
    包括数组、函数、对象。
  • 不可变类型(原始类型内容)
    包括 数字、字符串、布尔值、null、undefined 。
    在许多语言中,字符串都被看作引用类型,而非原始类型,因为字符串的长度是可变的。JavaScript 打破了这一传统。

堆(heap)和栈(stack)区别:

堆和栈都是内存中的一部分,有着不同的作用,而且一个程序需要在这片区域上分配内存。

区别 堆(heap) 栈(stack)
空间分配 大小需要自己申请,并指明大小 系统自动分配释放
存储区 队列优先,先进先出(FIFO—first in first out) 先进后出(FILO—First-In/Last-Out)
缓存方式 二级缓存,生命周期由虚拟机的垃圾回收算法来决定 一级缓存,被调用时处于存储空间中,调用完毕立即释放。
申请效率 由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便. 速度较快。但程序员是无法控制的。
那么问题来了,引用类型怎么比较是否相等呢?

比如两个单独的对象或数组必须比较它们的属性或元素:

function equalArrays(a,b){
    if(a.length != b.length) return false;  //两个长度不同的数组不相等
    for(var i = 0; i<a.length; i++){
        if(a[i] != b[i]) return false;
    }
    return true;
}
趁热理解浅拷贝和深拷贝

对于原始类型如字符串,浅拷贝是对值的复制,对于引用类型如对象来说,浅拷贝是对对象地址的复制。
如前面的买东西的例子:

  • 浅拷贝: 对于买水付的钱(原始类型)的复制,是真的钱;而买车是对银行卡(引用类型)的复制,两个卡都指向同一个账户,银行里的同一堆钱,而不是新开了一个账户(堆)。一个卡里面钱用了一点另外一个卡钱有会少。
  • 深拷贝: 买水付的钱(原始类型)的复制,是真的钱;而买车是对银行那堆钱(堆)的复制,变成了另外一堆钱,重新开一个账户办个卡(引用类型)对应着它,即使你把新卡钱花完了也不会影响之前的卡。