如何在JavaScript中实现数组?好的旧名单怎么了



JavaScript提供了各种可使用的数据结构,包括数组上的简单对象、集合、映射、弱变体以及ArrayBuffer。

在过去的半年里,我发现自己在现场重现了一些更常见的结构,如Dequeues、计数地图和大多数不同的树木变体。

在查看Ecma规范时,我找不到关于数组如何在内存级别上实现的描述,据说这取决于底层引擎?

与我习惯的语言相反,JavaScript中的数组具有可变长度,类似于列表。这是否意味着元素在内存中不一定相邻排列?如果达到某个阈值,拼接推送和弹出是否真的会导致新的分配,类似于Java中的ArrayLists?我想知道数组是否是队列和堆栈的方式,或者在某些情况下,引用下一个元素的实际列表实现是否适合JavaScript(例如,与数组的本机实现相比,关于开销?(。

如果有人有更深入的文献,请在这里链接。

在查看Ecma规范时,我找不到关于如何在内存级别上实现数组的描述,据说这取决于底层引擎?

ECMAScript规范没有指定或要求特定的实现。这取决于实现数组的引擎来决定如何最好地存储数据。

V8引擎中的数组根据数组的使用方式有多种形式。一个没有孔、只包含一种数据类型的顺序数组被高度优化为类似于C++中的数组。但是,如果它包含混合类型或包含孔(数组中没有值的块,通常称为稀疏数组(,它将具有完全不同的实现结构。而且,正如你所能想象的,如果数组中的数据发生变化,使其与当前优化的形式不兼容,那么它可能会从一种实现类型动态地更改为另一种

由于数组具有索引随机访问,因此它们在内部并没有实现为链表,因为链表没有有效的方法来进行随机索引访问。

增长数组可能需要重新分配更大的内存块,并将现有数组复制到其中。调用类似.splice()的东西来删除项将不得不将数组的部分向下复制到较低的位置。

将自己的链表实现用于队列而不是数组是否更有意义取决于一系列因素。如果队列变得很大,那么处理列表的单个分配可能会更快,因此避免为了操作它而不得不复制队列的大部分。如果队列从未变得很大,则在数组中移动数据的开销很小,链表的额外复杂性和其中涉及的额外分配可能不值得。

举个极端的例子,如果你有一个非常大的FIFO队列,那么它作为一个数组并不是特别理想,因为你会在一端添加项目,从另一端删除项目,这需要复制整个数组才能从底端插入或删除一个项目,如果长度定期更改,引擎可能也会定期重新分配。复制开销在你的应用程序中是否相关,需要通过实际的性能测试来测试,看看是否值得做点什么。

但是,如果您的队列始终是完全相同的数据类型,并且从未有任何漏洞,那么V8可以将其优化为C++风格的内存块,并且在调用.splice()时可以高度优化(使用CPU块移动指令(,这可以非常非常快。因此,您必须进行测试,以确定是否值得尝试在数组之外进行进一步优化。

下面是一个关于V8如何存储和优化阵列的精彩演讲:

V8 中的元素种类

以下是关于这个主题的其他参考文章:

JavaScript数组如何在后台中工作

V8阵列源代码

V8 的性能提示

V8如何优化大型阵列

最新更新