JavaScript 内存模型
最近我在学习JavaScript的过程中,对JavaScript的内存机制深感疑惑,所以查阅了一些资料和博客。将我了解到的分享出来,本人才疏学浅,希望有什么表述有误的地方,望指点一二。
JavaScript 的内存模型:调用栈和堆
- 栈(stack)是有结构的,每个区块按照一定次序存放(后进先出),栈中主要存放的是基本类型的变量的值以及指向堆中的数组或者对象的地址,存在栈中的数据大小与生存期必须是确定的。除此之外,还可以明确知道每个区块的大小,因此,stack的寻址速度要快于heap
- 堆(heap)是没有结构的,数据可以任意存放,它是用于存放复杂数据类型(引用类型)的,例如数组对象、object对象等。它和栈最大的区别是,堆可以保存无序、能够动态增删的数据——对于对象和数组来说,这是完美的存储空间。
1.JavaScript 原始数据类型的内存结构
JS中的基础数据类型,这些值都有固定的大小,往往都保存在栈内存中(闭包除外),由系统自动分配存储空间,原始数据类型(基本数据类型):Undefined、Null、Boolean、Number、String
注:每个变量都对应一块内存地址,变量名用来查找对应的内存,变量的值就是内存中保存的数据。
(1)以变量number赋值为28为例来理解
let number = 28;
执行这段代码的时候,JavaScript 会...
- 为你的变量(number)创建一个唯一标识符
- 为变量分配一个内存地址(运行时)
- 在分配的地址中存储一个值(28)
有上图可知,实际上从内存上看,因为变量是内存的标识,所以number对应地址值为0x0000A520的内存,其内存的值为28,所以number=28(也可理解number等于内存地址为0x0000A520,这内存保存值为28);
(2)若在声明一个新变量newValue,并且让newValue=number,那么内存结构会是什么样子??
有上图可知,newValue还是等于28,因为newValue也等于number指向的内存地址。
(3)若number=33,那么那么在对0x0000A520这个地址的值进行修改,还是其他????
如图,事实上,因为 JavaScript 的基本数据类型是不可变的,number=33会重新分配一个新的内存地址来存储这个值。
(也可理解为number修改变量的值,number改变了指针的指向)
2.JavaScript 引用数据类型的内存结构
JS的引用数据类型,比如数组Array,它们值的大小是不固定的。引用数据类型的值是保存在堆内存中的对象。JS不允许直接访问堆内存中的位置,因此我们不能直接操作对象的堆内存空间引用数据类型:Object,Function,Array等
以声明变量arr=[‘hello‘,23]为例
let arr=[‘hello’,25]
当 JS 引擎执行上面的代码,内存会发生如下变化:
- 为变量(arr)创建一个唯一标识符。
- 在栈中给变量分配一个地址a(运行时)。
- 在堆中分配一个地址b,用来存储值 [](运行时)。
- 地址a所存储的值为地址b
由上图可得,我们首先从栈中获取了该数组的地址指针,然后再从堆内存中取得我们需要的数据
为了更好的搞懂栈内存与堆内存,我们可以结合以下例子与图解进行理解。
var a1 = 0; // 栈 var a2 = ‘this is string‘; // 栈 var a3 = null; // 栈 var b = { m: 20 }; // 变量b存在于栈中,{m: 20} 作为对象存在于堆内存中 var c = [1, 2, 3]; // 变量c存在于栈中,[1, 2, 3] 作为对象存在于堆内存中
例子:
var person={name:"John"}; // person存放的是对象的地址值 var people=[{name:"John"}]; var morePeople=[person] console.log(people.indexOf(person));// -1 找不到 console.log(morePeople.indexOf(person)); // 0
分析:person、people,morePeople在栈中拿到的都是其对象的内存地址。
people想要查找其中有没有person这项没找到,既然表面上person的内容{name:"John"}与people的{name:John}内容相等,但是本质上他们是两个不同的对象,所以其内存地址是不同的,我们用indexOf(person)是通过找person的内存地址去引用这个对象的,并非通过数值。
morePeople可以找到,是因为本身数组的项就是存储的是person内容地址。
内存的生命周期
JS环境中分配的内存一般有如下生命周期:
- 内存分配:当我们申明变量、函数、对象的时候,系统会自动为他 们分配内存
- 内存使用:即读写内存,也就是使用变量、函数等
- 内存回收:使用完毕,由垃圾回收机制自动回收不再使用的内存