JS 中的数组

175 阅读2分钟

1. 标准数组

1.1 数组的定义

在计算机科学中,数组数据结构,简称数组(Array),是由相同类型的元素的集合所组成的数据结构,分配一块连续的内存来存储。利用元素的索引可以计算出该元素对应的存储地址。

Java

int[] arr = new int[3];

C/C++

int arr[3];

1.2 解释一些疑问

1.2.1 为什么要指明数据类型和大小?

首先在创建数组的时候,需要向内存申请一块空间,而这块空间的大小就是由数据类型和数组长度来决定的。比如上文中,int 类型是 4 个字节,长度为 3,那么就需要 4 * 3 = 12 个字节的内存空间。

在内存中的存放方式形如:

image.png

1.2.2 为什么数组长度不可变?

首先数组的内存是连续分配的,如果长度发生改变,那说明占用的内存空间也会发生改变,但是原空间的后面部分并不能保证是可用的。

1.2.3 为什么数组下标从 0 开始?

这都归结于数组的寻址方式:array[i]Address = headAddress + i * dataTypeSize。如果改为从 1 开始,当然是符合人类的直观逻辑,但是对于 CPU 来说,每次就需要多做一次减法。

2. JavaScript 中的数组

通过上一节的介绍,其实很明显地就能发现,JS 中的数组有点特殊,除了下标也是从 0 开始以外,其他的几乎都不太一样,甚至可以说是毫不相干。JS 中的数组,可以存储各种类型的数据并且也没有固定的长度。

2.1 为什么可以存储不同类型的值

因为,Array 继承自 Object。

2.2 JS 中的数组是怎么存储的?

const arr = [1];

arr[2000] = 2000;

// arr 会如何存储?

2.2.1 数组的模式

image.png 由注释可以看出,JSArray 分为两种模式:

  • 快模式,储存结构为 FixedArray,数组长度 <= 元素长度。push 和 pop 操作会被用来扩容/缩容。
  • 慢模式,储存结构为 HashTable,以数字为键值。

快、慢数组的区别

  1. 存储方式:快数组是连续存储的,慢数组是零散的。
  2. 内存使用:快数组因为是连续的,其中可能还存在空洞,所以比较费内存。慢数组不存在空洞,省内存。
  3. 遍历效率:快数组空间连续,遍历速度快。慢数组每次都要寻找 key 的位置,效率差一些。

2.2.2 数组的转换

  1. 快数组 => 慢数组 a. 新容量 >= 3 * 旧容量 * 3

image.png image.png image.png b. 新增索引与原来最大索引差值大于 1024 image.png image.png

  1. 慢数组 => 快数组

当能节省不少于 50% 的空间时,才会转换。 image.png

2.2.3 动态扩容和缩容

初始化一个空数组会预先分配四个元素的内存大小。 image.png image.png

扩容:

新容量 = 1.5 * 旧容量 + 16 image.png

缩容:

如果容量大于等于 length * 2 + 16,则进行缩容调整。通过判断 length + 1 == old_length 来确定是收缩一半容量还是全部。 image.png

2.3 数组中的元素类型

image.png

const arr1 = [1, 2, 3];   // PACKED_SMI_ELEMENTS
const arr2 = [1, 2.2, 3]; // PACKED_DOUBLE_ELEMENTS
const arr3 = [1, '2', 3]; // PACKED_ELEMENTS

const arr = [1, 2, 3]; // PACKED_SMI_ELEMENTS
arr[9] = 4;            // HOLEY_SMI_ELEMENTS

image.png

// bad
const arr = new Array(3); // HOLEY_SMI_ELEMENTS
arr[0] = 1; // HOLEY_SMI_ELEMENTS
arr[1] = 2; // HOLEY_SMI_ELEMENTS
arr[2] = 3; // HOLEY_SMI_ELEMENTS

// good
const arr = new Array(1, 2, 3);   // PACKED_SMI_ELEMENTS
const arr = [1, 2, 3];            // PACKED_SMI_ELEMENTS

但,这也不绝对。

image.png image.png

参考文档

  1. Elements kinds in V8
  2. V8 中的快慢属性与快慢数组