我们常常会听说什么栈内存、堆内存,那么他们到底有什么区别呢,在js中又是如何区分他们的呢,今天我们来看一下。
一、栈内存和堆内存的区分
一般来说,栈内存主要用于存储各种基本类型的变量,包括Boolean、Number、String、Undefined、Null...以及对象变量的指针,这时候栈内存给人的感觉就像一个线性排列的空间,每个小单元大小基本相等,栈内存中的变量一般都是已知大小或者有范围上限的,算作一种简单存储。
而堆内存主要负责像对象Object这种变量类型的存储,堆内存存储的对象类型数据对于大小这方面,一般都是未知的,(所以这大概也是为什么null作为一个object类型的变量却存储在栈内存中的原因)。
来一张图感受一下:
二、测试
Ⅰ.基本数据类型
/* 基本数据类型 */ var a = 1; var b = 1; console.log(a === b);//true var c = '桔子桑'; var d = '桔子桑'; console.log(c === d);//true
基本数据类型,因为都是存在栈内存中的,以上面的int为例:
var a = 1;变量 a 存在栈内存中,他的值是基本数据类型(int),自然也是在栈内存中,栈内存有没有1?没有那就拿出一块内存存1,这个变量a指向这块值为1的栈内存地址;
var b = 1;同理,变量 b 也是在栈内存中的,但是赋值的时候,发现,栈内存有一块地址存着int型的值1,那么就直接指向这块栈内存了;
所以最终 a === b 是 true;
Ⅱ.new 关键字生成的对象
/* new关键字 */ var a = new String('桔子桑'); var b = new String('桔子桑'); console.log(a === b);//fasle
new关键字生成的对象都是存在于堆内存中的,上述代码中:
var a = new String('桔子桑');变量 a 存在于栈内存中,他的值是一个指针,这个指针指向堆内存中的一个对象!
附上一段C代码,方便理解
int a = 20; /* 实际变量的声明 */int * ip; /* 指针变量的声明 */ ip = &a; /* 在指针变量中存储 变量a 的地址 */
所以我们应该这么理解:
普通变量的值类型是基本数据类型,指向栈内存中的一块地址;
引用类型变量的值是指针,指向堆内存中的一块地址。
Ⅲ.指针的赋值(引用类型)
/* 指针赋值 */ var a = new String('桔子桑'); var b = a; console.log(a === b);//true
我们看到,指针型变量 a 的值是一个指针,指向堆内存中一块地址;
然后 又定义了一个变量 b ,他的值等于 a,a 的值是什么?a 的值是一个指针啊,那么变量 b 就等于这个指针,自然也是指向堆内存的那一块地址咯。
图示:
Ⅳ.const关键字
我们知道const关键字用来定义一个常量,
const
实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址不得改动。
对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个栈内存地址,因此等同于常量。
但对于复合类型的数据(主要是对象和数组),变量指向的栈内存地址,保存的只是一个栈内存指针,const
只能保证这个指针是固定的,至于它指向的数据结构是不是可变的,就完全不能控制了。
所以,const保证的是栈内存这边变量与其值不能发生改变,所以对于复合类型,指针指向的堆内存中存的数据结构能否改变是左右不了的。
/* const关键字 */ const a = 1; a = 2; const b = { name:'jack' }; b.name = 'tom';
对于前者,因为常量 a 和他的值都是在栈内存中的,不能更改,手动修改只会报错:
而对于后者,因为常量 b 的值是一个指针,这个指针由于存在于栈内存,所以他的指向是不能更改的(堆内存地址),但是对应堆内存地址中存的数据结构(存了一个对象)是可以更改的。