前言
在了解javascript的变量存储机制之前需要了解javascript的数据类型,在js中,数据类型分为基本数据类型和引用数据类型。二者存在内存中,基本类型存在栈中,引用类型存储在堆里。
想查看javascript数据类型详细介绍请访问:[JavaScript数据类型总结](https://blog.csdn.net/honeymoon_/article/details/120303669)
javascript内存生命周期分为三个周期:内存分配(分配所需要的暂时使用的内存大小) ——> 使用期(读、写内存)——> 回收(对于不需要使用的内存将其释放)
var num = 1 // 内存分配
alert(num) // 内存使用
num = null // 内存回收
1. 栈、堆内存
JS的内存空间分为栈(stack)、堆(heap)、池(一般也会归类为栈中)。其中栈存放变量,堆存放复杂对象,池存放常量,所以也叫常量池。
1. 栈
在js中,栈中存放基本数据类型,并且具有按值访问,存储值大小固定,系统自动分配内存空间,空间小,运行效率高,先进后出等特点。
2. 堆
堆是一种经过排序的树形数据结构,每个结点都有一个值。通常我们所说的堆的数据结构,是指二叉堆。
在js中,堆中存放引用数据类型,具有按引用访问,存储值大小不固定,由代码指定分配内存空间,空间大,运行效率相对低,无需存储(因为根据引用获取)等特点。
引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。在获取引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。
- 引用类型的复制
let m = { a: 10, b: 20 };
let n = m;
n.a = 15;
console.log(m.a) // 15
m、n都是引用类型,栈内存中存放地址指向堆内存中的对象, 引用类型的复制会为新的变量自动分配一个新的值保存在变量中, 但只是引用类型的一个地址指针而已,实际指向的是同一个对象, 所以修改 n.a 的值后,相应的 m.a 也就发生了改变。
2. 闭包
闭包是指有权访问另一个函数作用域中变量的函数,函数外面的作用域可以访问函数内部的局部变量。
function fn() {
var num = 10;
function fun() {
conosle.log(num)
}
return fun;
}
var f = fn();
f(); // 相当于调用fun(),而fun() 可以访问fn内部的局部变量num。由此形成闭包,实现在函数外部访问内部局部变量。
闭包中的变量并不保存中栈内存中,而是保存在堆内存中。 可以避免使用全局变量,防止全局变量污染。但是可能会造成内存泄漏或溢出。
2. 垃圾回收
垃圾回收方面,栈内存变量基本上用完就回收了,而推内存中的变量因为存在很多不确定的引用,只有当所有调用的变量全部销毁之后才能回收。
垃圾回收机制有两种:1.标记清除, 2.引用计数
1.标记清除:js会对变量做一个标记yes or no的标签以供js引擎来处理,当变量在某个环境下被使用则标记为yes,当超出改环境(可以理解为超出作用域)则标记为no,然后对有no的标签进行释放。
2.引用计数:对于js中引用类型的变量, 采用引用计数的内存回收机制,当一个引用类型的变量赋值给另一个变量时, 引用计数会+1, 而当其中有一个变量不再等于值时,引用计数会-1, 如果引用计数为0, 则js引擎会将其释放掉。