JavaScript 中的变量,在内存中的具体存储形式,你知道吗?

423 阅读5分钟

这是我参与8月更文挑战的第4天,活动详情查看8月更文挑战

前言

在 JavaScript 中,每一个变量在内存中都需要一个空间来存储。

通过对内存空间的学习,以此为基础,一步一步的理解 JavaScript 的执行上下文、作用域链、闭包、原型链等重要概念。后面会输出相关文章。

基础不牢,地动山摇。

内存空间又被分为两种:栈内存与堆内存。下面我们就 JavaScript 的内存空间,开启我们的探索之旅。

了解常见数据结构

与 C/C++ 不同,JavaScript中并没有严格意义上区分栈内存与堆内存。

要怎么理解栈呢?我们先来看一张图:

image.png 上面这张图片,碟子的存放方式与栈中存取数据的方式,是一样的。放在最上面的碟子,它一定是最后被放上去的,但可以最先被拿出来使用,这就是后进先出。 如果我们想要拿最底下的碟子,从计算机的角度出发,就必须要先把上面的碟子一个一个的拿开,让最底下的碟子,呈现在你的面前。这就是先进后出。 栈内存的特点就是先进后出,后进先出,其实只要记住,一个就行,比如记住“先进后出”。

堆数据结构是一种树状结构。它的存取数据的方式,则与书架与书非常相似。

怎么理解堆呢?我们先来看一张图

image.png 大家都去过图书馆吧,要怎么找到自己想要的书呢?可以在电脑上搜索书名:“JavaScript 高级程序设计”,获取到馆藏地点和索引号,就可以找到自己想要的书籍,就不用像上面像栈那样这么麻烦。

image.png

队列

队列是一种先进先出(FIFO)的数据结构 我们先来看一张图: image.png 比如去电影票排队买票一样,排在前面的人肯定是先买到票的,后面的,是后买到票。

栈内存

特点:

  • 存储基本数据类型
  • 按值访问
  • 存储的值大小固定
  • 由系统自动分配内存空间
  • 空间小,运行效率高
  • 先进后出

存储基础数据类型

基础数据类型,也叫作值类型。值类型由于结构相对简单,直接把创建的值存储到“栈内存”中即可,所以栈内存有两个作用:

  1. 提供代码执行的环境
  2. 存储基本数据类型值

按值访问

按值访问,相当于是将原数据的值进行一次拷贝,赋值给新的变量,原来的变量发生改变后,新变量是不会变的。我们可以结合例子来理解:

var a = 1.23;
var b = a;
a = 3.14;
console.log(a); // 输出结果?
console.log(b); // 输出结果?

结果,如图: 因为是按值访问,所以 b 还是 1.23,不会发生改变。 image.png

存储的值大小固定

JavaScript中的原始类型的值被直接存储在栈中,在变量定义时,栈就为其分配好了内存空间。 由于栈中的内存空间的大小是固定的,那么注定了存储在栈中的变量就是不可变的。

空间小,运行效率高

栈内存分配运算内置于处理器的指令集中,它的运行效率一般很高,但是分配的内存容量有限。

堆内存

特点:

  • 存储引用数据类型
  • 按引用访问
  • 存储的值大小不固定,可动态调整
  • 由代码进行指定分配
  • 空间大,运行效率相对较低
  • 无序存储,可根据引用直接获取

存储引用数据类型

引用数据类型的结构相对复杂(是一个综合体,包含很多值),所以不能直接存储在栈内存中,需要

单独开辟空间来存储,这个空间就是“堆内存”,引用数据类型值都存储在单独开辟的“堆内存”中!

堆内存只有一个作用:存储引用数据类型的值。

按引用访问

JavaScript 不允许直接访问堆内存中的数据,因此我们不能直接操作对象的堆内存空间,在操作对象的时候,实际上是在操作对象的引用而不是实际的对象,这里的引用,要怎么理解呢?可以理解为一个地址,这个地址跟堆内存的实际值有联系。

上面说的地址,就好比就是仓库的钥匙,堆内存的实际值,就好比是大仓库。对象,是可以往里面添加、修改属性,其实跟你有仓库的钥匙,就可以往里面放东西差不多。

还是结合例子加以理解吧

var obj = { name: '追梦玩家' };
var obj2 = obj;
obj2.name = '砖家';
console.log(obj.name); // 输出结果?

当我们复制引用类型的变量时,其实是复制的是栈中存的地址,所以复制出来的 obj2 和 实际上和 obj 指向同一个堆内存的实际值。所以我们修改了 obj2,obj 这个变量也会受影响,反之,也是一样的。 结果,如图:

image.png

存储的值大小不固定,可动态调整

上面这个特点,要怎么理解呢?看下下面代码

var obj = {};
obj['name'] = '追梦玩家';
obj['age'] = 18;
obj['sex'] = 'male';

比如,上面我们先声明一个对象,然后新增对象属性,进行赋值。你可以一直往里面放东西。

栈内存和堆内存的区别

image.png