JS基础拾遗 - 栈内存和堆内存

基本数据类型和引用数据类型

要准确把握JS中基本数据类型(number, string, null, undefined, boolean)和引用类型(Object)的区别,
就要对栈内存和堆内存的区别有一定理解。
栈内存:按值存放,空间大小确定,系统自动释放,可以直接访问,存取快;
堆内存:大小不定,不会自动释放,存取较慢;

基本数据类型的值存储于栈内存中,而引用数据类型的值则存储于堆内存;

基本数据类型和栈内存(Stack)

按值存放

由于栈内存是按值存放的,因此赋值操作都会开辟一个新的内存空间,比如

var a = 3;
// 或者
var b = a;

这里的b和a在赋值后是不会相互影响的,因为它们是内存上的两个不同区域,其中存储着它们各自的值;

栈内存
空间1 3
空间2 3
var b = 4;  // 修改b的值
栈内存
空间1 3
空间2 4

不可突变

基本类型数据的值都是不可突变的,对它们的任何修改都会开辟新的内存空间来存放新的值,
对于number、boolean这样的类型来说,以下过程是很自然的:

var a = 3;
栈内存
空间 3
a = 4;
栈内存
旧空间 3
新空间 4

但对于string类型来说,就有点让人难以理解了:
JS就为string提供了很多局部修改的操作(严格来说是String包装类型的方法),比如concat、slice等,
但基础较好的朋友应该也知道,要实现用这些方法修改字符串,都必须接收其返回值

var str = "hello";
var str = str.slice(0,2);  // 开辟新空间,接收新的值
栈内存
旧空间 “hello”
新空间 “he”

也就是说,string类型的值也是不可变的,类似下面的操作是无效的:

var str = "hello";
str[0] = "e"; //  试图像数组那样修改字符串
console.log(str);  // 仍返回 "hello"

也因此,JS提供给字符串的索引读取API(String.charAt())只能用于访问,不能用于修改字符串。

引用数据类型和堆内存(Heap)

JS中的对象和数组都是典型的引用类型数据,引用类型变量是一个指针,
指针的值(地址)储存在栈内存中,而指针则指向了堆内存的某个区域,
这就不难理解为什么引用类型光靠赋值是不能实现拷贝的:

var obj = {};
var obj2 = obj;
obj === obj2; // true
栈内存
obj 指向堆区A
obj2 指向堆区A

这说明,赋值操作确实让变量obj2开辟了新的空间,
但它实际存放了一个与变量obj一样的指针值,它们指向堆区的同一块内存空间
因此当我们分别用obj和obj2来修改对象属性时,我们访问到的是同一个堆区的数据;
也因此用==、===操作符来比较两个变量时,由于按值比较,返回的结果总是true

所以,对于引用类型的数据,赋值操作是不能完成拷贝的。

你可能感兴趣的:(前端,javascript)