这是我参与8月更文挑战的第4天,活动详情查看8月更文挑战
前言
在 JavaScript 中,每一个变量在内存中都需要一个空间来存储。
通过对内存空间的学习,以此为基础,一步一步的理解 JavaScript 的执行上下文、作用域链、闭包、原型链等重要概念。后面会输出相关文章。
基础不牢,地动山摇。
内存空间又被分为两种:栈内存与堆内存。下面我们就 JavaScript 的内存空间,开启我们的探索之旅。
了解常见数据结构
栈
与 C/C++ 不同,JavaScript中并没有严格意义上区分栈内存与堆内存。
要怎么理解栈呢?我们先来看一张图:
上面这张图片,碟子的存放方式与栈中存取数据的方式,是一样的。放在最上面的碟子,它一定是最后被放上去的,但可以最先被拿出来使用,这就是后进先出。
如果我们想要拿最底下的碟子,从计算机的角度出发,就必须要先把上面的碟子一个一个的拿开,让最底下的碟子,呈现在你的面前。这就是先进后出。
栈内存的特点就是先进后出,后进先出,其实只要记住,一个就行,比如记住“先进后出”。
堆
堆数据结构是一种树状结构。它的存取数据的方式,则与书架与书非常相似。
怎么理解堆呢?我们先来看一张图
大家都去过图书馆吧,要怎么找到自己想要的书呢?可以在电脑上搜索书名:“JavaScript 高级程序设计”,获取到馆藏地点和索引号,就可以找到自己想要的书籍,就不用像上面像栈那样这么麻烦。
队列
队列是一种先进先出(FIFO)的数据结构 我们先来看一张图:
比如去电影票排队买票一样,排在前面的人肯定是先买到票的,后面的,是后买到票。
栈内存
特点:
- 存储基本数据类型
- 按值访问
- 存储的值大小固定
- 由系统自动分配内存空间
- 空间小,运行效率高
- 先进后出
存储基础数据类型
基础数据类型,也叫作值类型。值类型由于结构相对简单,直接把创建的值存储到“栈内存”中即可,所以栈内存有两个作用:
- 提供代码执行的环境
- 存储基本数据类型值
按值访问
按值访问,相当于是将原数据的值进行一次拷贝,赋值给新的变量,原来的变量发生改变后,新变量是不会变的。我们可以结合例子来理解:
var a = 1.23;
var b = a;
a = 3.14;
console.log(a); // 输出结果?
console.log(b); // 输出结果?
结果,如图:
因为是按值访问,所以 b 还是 1.23,不会发生改变。
存储的值大小固定
JavaScript中的原始类型的值被直接存储在栈中,在变量定义时,栈就为其分配好了内存空间。
由于栈中的内存空间的大小是固定的,那么注定了存储在栈中的变量就是不可变的。
空间小,运行效率高
栈内存分配运算内置于处理器的指令集中,它的运行效率一般很高,但是分配的内存容量有限。
堆内存
特点:
- 存储引用数据类型
- 按引用访问
- 存储的值大小不固定,可动态调整
- 由代码进行指定分配
- 空间大,运行效率相对较低
- 无序存储,可根据引用直接获取
存储引用数据类型
引用数据类型的结构相对复杂(是一个综合体,包含很多值),所以不能直接存储在栈内存中,需要
单独开辟空间来存储,这个空间就是“堆内存”,引用数据类型值都存储在单独开辟的“堆内存”中!
堆内存只有一个作用:存储引用数据类型的值。
按引用访问
JavaScript 不允许直接访问堆内存中的数据,因此我们不能直接操作对象的堆内存空间,在操作对象的时候,实际上是在操作对象的引用而不是实际的对象,这里的引用,要怎么理解呢?可以理解为一个地址,这个地址跟堆内存的实际值有联系。
上面说的地址,就好比就是仓库的钥匙,堆内存的实际值,就好比是大仓库。对象,是可以往里面添加、修改属性,其实跟你有仓库的钥匙,就可以往里面放东西差不多。
还是结合例子加以理解吧
var obj = { name: '追梦玩家' };
var obj2 = obj;
obj2.name = '砖家';
console.log(obj.name); // 输出结果?
当我们复制引用类型的变量时,其实是复制的是栈中存的地址,所以复制出来的 obj2 和 实际上和 obj 指向同一个堆内存的实际值。所以我们修改了 obj2,obj 这个变量也会受影响,反之,也是一样的。 结果,如图:
存储的值大小不固定,可动态调整
上面这个特点,要怎么理解呢?看下下面代码
var obj = {};
obj['name'] = '追梦玩家';
obj['age'] = 18;
obj['sex'] = 'male';
比如,上面我们先声明一个对象,然后新增对象属性,进行赋值。你可以一直往里面放东西。