js对变量的存储主要有两种方式:栈内存和堆内存。
栈内存主要存储基本数据类型的变量(一般都是已知大小或者范围有上限),包括Boolean、Number、String、undefined、null,以及对象的指针。
堆内存主要存储对象等引用类型(大小未知)。

所以说,我们在定义基本数据类型的值时,js会把它的值存储进栈内存中,可以直接去改变它的值;
而当我们定义一个引用数据类型时,此时是分为两个步骤,
首先因为我们的值大小是不固定的,所以需要存进堆内存中,其次js会创建一个指针(也就是一个key,对应堆内存中的值),指向存进去的这块数据,然后把这个指针存进栈内存中,通俗讲就是js在栈内存中存储了当前引用数据类型的堆内存地址,我们使用某个对象时其实是通过这个内存地址去引用堆内存中的值。
举例:
- 当我们定义一个const对象的时候,我们说的常量其实是指针,就是const对象对应的堆内存指向是不变的,但是堆内存中的数据本身的大小或者属性是可变的。而对于const定义的基础变量而言,这个值就相当于const对象的指针,是不可变。
- 使用new关键字初始化的之后是不存储在栈内存中的。为什么呢?new大家都知道,根据构造函数生成新实例,这个时候生成的是对象,而不是基本类型。
var a = new String('123')
var b = String('123')
var c = '123'
console.log(a==b, a===b, b==c, b===c, a==c, a===c)
>>> true false true true true false
console.log(typeof a)
>>> 'object'
new一个String,出来的是对象,而直接字面量赋值和工厂模式出来的都是字符串。但是根据我们上面的分析大小相对固定可预期的即便是对象也可以存储在栈内存的,比如null,为啥这个不是呢?再继续看
var a = new String('123')
var b = new String('123')
console.log(a==b, a===b)
>>> false false
很明显,如果a,b是存储在栈内存中的话,两者应该是明显相等的,就像null === null是true一样,但结果两者并不相等,说明两者都是存储在堆内存中的,指针指向不一致。
说到这里,再去想一想我们常说的值类型和引用类型其实说的就是栈内存变量和堆内存变量,再想想值传递和引用传递、深拷贝和浅拷贝,都是围绕堆栈内存展开的,一个是处理值,一个是处理指针。
内存分配和垃圾回收
一般来说栈内存线性有序存储,容量小,系统分配效率高。而堆内存首先要在堆内存新分配存储区域,之后又要把指针存储到栈内存中,效率相对就要低一些了。
垃圾回收方面,栈内存变量基本上用完就回收了,而推内存中的变量因为存在很多不确定的引用,只有当所有调用的变量全部销毁之后才能回收。
网友评论