JS的基本数据类型和引用数据类型内存存储系列(一)

3,240 阅读4分钟

JS内存和其他语言的差别

在其他常见的语言中,用来保存变量和对象的内存一般分为栈内存和堆内存,而在JavaScript中,所谓的栈和堆都是放在堆内存中的,而在堆内存中,JS把其分为栈结构和堆结构,这里常被误认为是堆内存和栈内存,但是我们可以把它简称为栈和堆。

image.png

栈和堆的特点和区别

栈(stack) :栈会自动分配内存空间,会自动释放,存放基本类型和引用数据类型的变量

所有在函数/方法中定义的变量都是放在栈内存中,随着函数/方法的执行结束,这个函数/方法的内存栈也自然销毁。

优点:存取速度比堆快。 缺点:存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。

堆(heap) :动态分配的内存,大小不定也不会自动释放,存放引用类型的对象,指那些可能由多个值构成的对象,保存在堆内存中。

堆内存中的对象不会随方法的结束而销毁,即使方法结束后,这个对象还可能被另一个引用变量所引用(参数传递)。创建对象是为了反复利用。

基本数据类型的存储

基本数据类型被保存在栈结构中,在栈结构中会有全局执行环境(也称全局执行上下文),在全局作用域中的基本数据类型就是被保存全局执行环境中的。

var a = 10;
var b = a;
b = 20;
console.log(a); 
console.log(b); 

下图演示了这种基本数据类型赋值的过程:

image.png

首先var 定义的变量a 加入到全局执行环境中,并且赋值为10,然后定义变量b并且把a的值赋值给变量b,此时b的值为10,最后b的值被修改为20,最终的执行结果为a输出为10,b输出为20 注:实际上是有变量提升,先定义后赋值,不过理解存储原理变量提升可以暂且忽略,后面会介绍

引用数据类型的存储

引用类型是存放在堆内存中的对象,变量其实是保存的在栈内存中的一个指针(保存的是堆内存中的引用地址),这个指针指向堆内存。

引用类型数据在栈内存中保存的实际上是对象在堆内存中的引用地址。通过这个引用地址可以快速查找到保存中堆内存中的对象。

对比上一段代码,如果我们把10和20换成一个数组又会发生什么呢?

var arr = [1,2,3];
var arr1 = arr;
arr1[1] = 22;

console.log(arr);
console.log(arr1);

下图演示了这种引用数据类型的类型赋值的过程:

image.png

首先定义了arr变量,数组也是一个对象,所以在栈结构的全局执行环境中我们保存的实际上是一个地址值(指针),而真正的数组是被保存在了堆结构中。保存对象首先会在堆结构中开辟一块内存空间,然后把地址值赋值给变量,栈结构中只保存对象的地址值。
这里我们可以看到arr的保存的地址值赋值给了arr1,所以此时arr和arr1都指向了同一个堆结构中的对象,此时我们通过数组的索引arr[1]去修改第二个元素为22,无论我们通过arr还是arr1访问这个数组,实际上都是在访问相同的对象。
所以我们两次打印输出的结果都是[1,22,3]。

练习思考:

感兴趣的小伙伴可以思考一下下面的代码如何画图以及输出的结果是什么?欢迎留言批评指正哦!~

  var arr = [1,2,3];
  var arr1 = arr;
  arr = [1,22,3];
  console.log(arr1);
//基本数据传参
var a = 10;
var b = 20;
function add(a,b){
  a = 40;
  return a + b;
}
console.log(add(a,b));
console.log(a,b);
//对象数据类型传参
var arr = [1,2,3];
function fn(arr){
  for(var i = 0; i < arr.length; i++){
      arr[i] += 2;
  }
}
fn(arr);
console.log(arr);