栈(stack):主要存放的是基本类型的变量和对象的应用,其优势是存储速度比堆快,缺点是存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。
堆(heap):用于引用类型(复杂数据类型:如数组对象、object对象)分配空间,运行时动态分配内存,存储速度较慢。
JavaScript中,内存分为三种类型:代码空间、栈空间、堆空间,其中代码空间用于存放可执行代码。
一、堆栈内存空间
1、栈内存空间
用栈作为数据结构在内存中所申请的空间。
2、栈的特点:
1、后进先出,最后添加进栈的元素最先出
2、访问栈底元素,必须拿掉它上面的元素
js7种基本数据类型变量保存在栈内存中,因为基本数据类型占用空间小、大小固定,通过值来访问,属于被频繁使用的数据。
3、闭包
闭包中的基本数据类型变量是保存在堆内存里的,当函数执行完弹出调用栈后,返回内部函数的一个应用,这时候函数的变量就会转移到堆上,因此内部函数依然能访问到上一层函数的变量。
二、堆内存空间
用堆作为数据结构在内存中所申请的空间。
通常情况下,我们所说的堆数据结构指的就是二叉堆。
1、二叉堆的特点:
1、它是一颗完全二叉树
2、二叉堆不是最小堆就是最大堆
2、引用数据类型
引用数据类型存储在堆内存中,引用数据类型占据空间大、大小不固定,如果存储在栈中,将影响程序的运行性能。
引用数据类型会在栈中存储一个指针,这个指针指向堆内存空间中该实体的起始地址。
当解释器寻找引用值时,会先检索其在栈中的地址,取得地址后,从堆中获得实体。
1 |
|
上面代码中:
1 |
|
堆内存空间中的object,表示的是存储在空间中的其他对象的引用值
3、栈内存空间与堆内存的区别
堆内存空间:相当于一个采用二叉堆作为数据结构的容器。
堆内存:指的是一个引用类型的具体值
堆内存存在于堆内存空间中
三、变量复制
1、基本数据类型的复制
下面代码中,name、alias都是基本类型,值存储在栈内存,分别有各自独立的栈空间,因此修改了alias的值,name是不受影响的。
1 |
|
相当于复制前是这样的:
name | jude
复制后:
alias | jude
name | jude
修改后:
alias | summer
name | jude
2、引用类型的复制
下面代码中,info、book都是引用类型,它们引用存在栈内存,值存在堆内存,它们的值指向同一块堆内存,栈内存中会复制一份相同的引用。
1 |
|
四、深拷贝与浅拷贝
1、浅拷贝:
引用数据类型在复制时,改了其中一个数据的值,另一个数据的值也会跟着改变,这种拷贝方式我们称为浅拷贝。
1.1 Object.asign()
Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。
1 |
|
1.2 扩展运算符
1 |
|
1.3 Array.prototype.concat()/Array.ptototype.slice()
1 |
|
2、深拷贝:
引用类型复制到新的变量后,二者是独立的,不会因为一个的改变而影响到另一个
实际上就是重新在堆内存中开辟一块新的空间,把原对象的数据拷贝到这个新地址空间里来
2.1 深拷贝方法1:
将对象转一遍JSON,缺点是只能转化一般常见的数据,function、undefined、正则等类型无法通过这种方法变回来。
1 |
|
2.2 深拷贝方法2:
手动去写循环遍历
1 |
|