JavaScript数组在内存中是如何存储的?

7

我在思考JavaScript中数组在内存中的存储方式。

我已经阅读了如何在物理内存中表示JavaScript数组,但我没有找到我的答案。

我想知道的更多是有关数组单元的内存位置。例如,在C语言中,定义数组时需要定义数组大小。这样,C将定义整个内存块,并且可以查看每个单元的确切位置。

例如:

int array[10]; // C knows the memory location of the 1st item of the array

array[3] = 1   // C can do that, because it can calculate the location 
               // of array[3] by doing &array + 3 * (int size)

在JS中,你可以在为其他东西分配内存之后增加数组的大小,这意味着JS不使用“块”类型的数组。但是,如果数组不是一块连续的内存,那么JS如何计算每个单元的位置呢?JS数组遵循链表类型结构吗?

这个链接可能会有帮助:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array - evolutionxbox
3
我不明白为什么重复问题的答案不能回答这个问题。 - Kevin B
2
这取决于具体的实现。语言规范并不强制引擎必须如何存储数组,只规定它们的行为。 - Paul
1
@evolutionxbox 这是一个API参考,而不是关于数组在内部如何实现的参考。 - Scott Marcus
1
可能不是这样,但据我所知,标准只定义了数组应该具有的接口,具体模型留给实现。另一方面,ArrayBuffer旨在成为连续的内存块,这是为了性能考虑而精确设计的。 - Máté Safranka
显示剩余2条评论
2个回答

5

我建议大家关注一件事情,那就是node.js最近成为了Chrome V8的一等公民,因此我建议学习V8,不仅可以了解它如何处理这些实现细节,还可以了解其原因。

首先,本文应该对读者有益,因为它专注于编写优化的同构JavaScript代码:

https://blog.sessionstack.com/how-javascript-works-inside-the-v8-engine-5-tips-on-how-to-write-optimized-code-ac089e62b12e

上面的文章详细介绍了JIT(即时编译器)是如何工作的,因此您应该能够在阅读后得出确切的问题。
以下是摘录:
阵列:避免稀疏数组,其中键不是增量数字。没有每个元素的稀疏数组是哈希表。访问这种数组中的元素更加昂贵。此外,尽量避免预分配大型数组。最好随着进展而增长。最后,不要删除数组中的元素。它使密钥稀疏。
其次,我还建议先阅读此内容,然后再从V8的角度出发: http://www.jayconrod.com/posts/52/a-tour-of-v8-object-representation 第三点,作为关键的附加事实,我一段时间前读了这个答案,现在时不时地会回想起来。我非常惊讶我现在才发现它。我字面上谷歌搜索了“stack overflow optimize train tracks”并找到了它。感谢 Google: 为什么处理排序数组比处理未排序数组快? 是的,那个答案确实有27,000个赞。
那篇文章谈到了分支预测,我希望你意识到这一点,因为它可能对你处理数据的方式产生一些影响,不仅仅是数组。再次注意我链接的第一篇文章,并注意它描述对象键的顺序时所说的内容。
通过理解实现细节并理解为什么问题被以那种方式解决,可以优化性能。
最后,除了标量值(我们称之为原始值 - String、Number、Boolean等),JavaScript 中的所有东西都是对象。
以下是一个引人思考的例子:
const arr = ['one', 'two', 'three']

const sameArr = {
  0: 'one',
  1: 'two',
  2: 'three',
}

我们可以像处理对象一样解构数组:

const yolo = ['one', 'two', 'three']

const {
  0: one,
  1: two,
  2: three,
} = yolo

console.log('Pretty cool:', one, two, three)

你可以从这个例子中得到一些提示,了解为什么改变键的顺序可能会对底层哈希表造成破坏。仅仅因为你看不到键并不意味着它们不存在或者不受影响。
在上面的例子中,如果它是一个映射,你可以使用 sameArr.get('0') 来获取数值表中确切的位置。
我还建议小心阅读旧版JavaScript材料,因为ES6进行了大量重构。我觉得更适合引导您的是V8材料。

4

与C或其他专有的编译语言不同,JavaScript是一种ECMAScript实现。其实现的细节并未标准化,而是特定于每个供应商的实现。简而言之,语言是如何实现的底层细节是一个黑匣子,虽然可以深入特定供应商的内部实现,但这方面没有标准,实现会因供应商而异。


6
我要下投票,因为虽然这个回答在技术上是正确的,但推理完全荒谬。数组的行为方式与语言是否专有没有任何关系。JavaScript中的数组工作方式是因为语言的设计如此。这与该设计的规范方式或是否规定了规范没有关系。更糟糕的是,这与语言是否编译无关,而这反过来也与语言是否专有无关。 - Aluan Haddad
1
@AluanHaddad 这并不是荒谬的。我提到专有语言是因为这些语言的编译器不受第三方实现的限制。而 JavaScript 则完全依赖于任何实现都能产生正确结果,如何实现这些结果并没有被任何人规定。 - Scott Marcus
1
这不是我的观点。一种语言可以在强制执行类似于C的数组行为的同时具有规范和实现之间相同的关系。这与编译与解释无关。 - Aluan Haddad
1
C是一种ISO标准语言,而不是专有语言。此外,C没有标准实现(更不用说单一实现了)。最好的两个实现是Microsoft Visual C和GNU C编译器,但还有其他实现,如Keil、Microchip、clang、SDCC等。因此,C和JavaScript非常相似-两者都没有标准的单一实现,并且都由竞争对手独立实现。尽管如此,数组的实现方式仍然存在很大差异。 - slebetman
1
实际上,与ECMAScript不同,C的各种标准具有非常具体的语言,明确允许供应商进行创新并实现与其他实现不同的事物:未定义的行为和实现定义的行为。 - slebetman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接