回顾js数据类型有哪些?

ECMAScript 变量可能包含两种不同数据类型的值:基本类型值和引用类型值。基本类型值指的是简单的数据段,而引用类型值指那些可能由多个值构成的对象。

复习一下两种数据类型各有哪些:
  1. 基础类型(Undefined, Null, Boolean, Number, String, Symbol(ES6新添加的))一共6种

  2. 引用类型(Object)

Object 中包含了哪几种类型?

其中包含了Data、function、Array等。这三种是常规用的。

奇怪的知识:
谷歌67版本中还出现了一种 bigInt。是指安全存储、操作大整数。关于bugInt网上也有很多类型的功能实现的插件,是专门用于js处理大数据用的,以弥补js精度丢失的问题。

那么这些数据在程序运行时是存在哪里?

堆栈

首先了解一下Javascript的堆栈概念

栈(stack)

栈的特点是**”LIFO,即后进先出(Last in, first out)”**。数据存储时只能从顶部逐个存入,取出时也需从顶部逐个取出。比如一个乒乓球的盒子,先放进去(入栈)的乒乓球就只能后出来(出栈)。

堆(heap)

堆的特点是**”无序”key-value“键值对”**存储方式。
举个例子:书架存书

我们想要在书架上找到想要的书,最直接的方式就是通过查找书名,书名就是我们的key。拿着这把key,就可以轻松检索到对应的书籍。**”堆的存取方式跟顺序没有关系,不局限出入口”**。

堆是在程序运行时,而不是在程序编译时,申请某个大小的内存空间。即动态分配内存,对其访问和对一般内存的访问没有区别。对于堆,我们可以随心所欲的进行增加变量和删除变量,不用遵循次序。

堆栈在js中的应用

JS中的基本数据类型的值都有固定长度,它们往往都保存在栈内存中(闭包除外),由系统自动分配存储空间。我们可以直接操作保存在栈内存空间的值。
JS的引用数据类型,它们值的大小是不固定的。比如:

1
2
3
4
5
let arr = [];
arr.push(1);
arr.push('abc);
arr.push(true);
...

引用数据类型的值是保存在堆内存中的对象。JS不允许直接访问堆内存中的位置,因此我们不能直接操作对象的堆内存空间。在操作对象时,实际上是在操作对象的引用而不是实际的对象。因此,引用类型的值都是按引用访问的。这里的引用,我们可以粗浅地理解为保存在栈内存中的一个地址,该地址与堆内存的实际值相关联。

数据类型访问&复制

基本数据类型:基本数据类型值指保存在栈内存中的简单数据段。访问方式是按值访问。
let a = 1;
栈内存

| :-: | :-: |
| a | 1 |
| | |

操作的是变量实际保存的值。
a = 2 ;

| :-: | :-: |
| a | 2 |
| | |

复制变量值

除了保存的方式不同之外,在从一个变量向另一个变量复制基本类型值和引用类型值时,也存在不同。如果从一个变量向另一个变量复制基本类型的值,会在变量对象上创建一个新值,然后把该值复制到为新变量分配的位置上。
let b = 5;
let c = b;

| :-: | :-: |
| b | 5 |
| c | 5 |

引用数据类型:引用数据类型值指保存在堆内存中的对象。也就是,变量中保存的实际上的只是一个指针,这个指针指向内存中的另一个位置,该位置保存着对象。访问方式是按引用访问。
let a = new Object();
let b = a;

引用类型变量的复制:当从一个变量向另一个变量复制引用类型的值时,同样也会将存储在变量对象中的值复制一份放到 为新变量分配的空间中。不同的是,这个值的副本实际上是一个指针,而这个指针指向存储在堆中的一 个对象。复制操作结束后,两个变量实际上将引用同一个对象。因此,改变其中一个变量,就会影响另 一个变量;

因此深浅拷贝可以这样区分:
“浅拷贝:栈存储拷贝”
“深拷贝:栈堆存储拷贝”
深拷贝会同时开辟新的栈内存,堆内存空间。

为什么会有栈内存和堆内存之分?

通常与垃圾回收机制有关。为了使程序运行时占用的内存最小。

当一个方法执行时,每个方法都会建立自己的内存栈,在这个方法内定义的变量将会逐个放入这块栈内存里,随着方法的执行结束,这个方法的内存栈也将自然销毁了。因此,所有在方法中定义的变量都是放在栈内存中的;

当我们在程序中创建一个对象时,这个对象将被保存到运行时数据区中,以便反复利用(因为对象的创建成本通常较大),这个运行时数据区就是堆内存。堆内存中的对象不会随方法的结束而销毁,即使方法结束后,这个对象还可能被另一个引用变量所引用(方法的参数传递时很常见),则这个对象依然不会被销毁,只有当一个对象没有任何引用变量引用它时,系统的垃圾回收机制才会在核实的时候回收它。

垃圾回收机制

JavaScript中有自动垃圾回收机制,会通过标记清除的算法识别哪些变量对象不再使用,对其进行销毁。开发者也可在代码中手动设置变量值为null(a = null)进行标记清除,让其失去引用,以便下一次垃圾回收时进行有效回收。

局部环境中,函数执行完成后,函数局部环境声明的变量不再需要时,就会被垃圾回收销毁(理想的情况下,闭包会阻止这一过程)。

全局环境只有页面退出时才会出栈,解除变量引用。所以开发者应尽量避免在全局环境中创建全局变量,如需使用,也要在不需要时手动标记清除,将其内存释放掉。