JavaScript数组实际上是以数组的形式实现的吗?

21
JavaScript中的ArrayObject之间的区别并不是很大。实际上,Array主要添加了length字段,因此您可以将ArrayObject都用作数字数组:
var ar = new Array();
ar[0] = "foo";
ar["bar"] = "foo";

var ob = new Object();
ob[0] = "foo";
ob["bar"] = "foo";

assert(ar[0] == ob[0] == ar["0"] == ob["0"] == ar.bar == ob.bar); // Should be true.

所以我的问题是,在流行的JavaScript引擎(V8,JavaScriptCore,SpiderMonkey等)中,如何处理这个问题?显然,我们不希望我们的数组实际上被存储为具有键值的哈希映射!我们如何相对确定我们的数据被存储为一个真正的数组?
就我所见,引擎可以采取以下几种方法:
  1. ArrayObject完全相同 - 作为具有字符串键的关联数组实现。
  2. Array是一种特殊情况,具有类似于std::vector的数组支持数字键,并且具有一些密度启发式算法,以防止在执行ar[100000000] = 0;时出现疯狂的内存使用。
  3. ArrayObject相同,并且所有对象都会获得一种启发式算法,以查看是否使用数组更有意义。
  4. 我没有想到的非常复杂的东西。

如果有一个适当的数组类型(咳嗽WebGL类型数组咳嗽),那么这将更加简单。


2
这篇文章有点老了,而且它并没有明确解释实现。然而,它进行了详细的性能测量,并推断出可能的实现方式。 - Matthew Flaschen
2
数组并不仅仅是一个带有length属性的映射。如果是这样,那么移位或插入操作将打破索引(例如,从数组中移除一个值,它仍然从索引0开始,而不是1)。因此,至少还有一些其他的事情发生了。(当然,这并不一定说明实现存在问题) - Flambino
1
你为什么期望 r[0] == ob[0] == ar["0"] == ob["0"] == ar.bar == ob.bar 为真? 'a' == 'a' == 'a' 是假的,因为它会被求值为 true == 'a',然后再被求值为 false - Mike Samuel
4
@Flambino,那不是真的。shift被实现为通用方法,而不是仅适用于数组的特定方法。它可以很好地与普通对象一起使用。移除操作之所以有效是因为它获取第一个值,然后循环遍历所有值进行左侧赋值,最后删除最后一个元素并设置长度。 - Mike Samuel
1
@Flambino,尝试运行var a = { 0: 0, 1: 1, length: 2 }; Array.prototype.shift.apply(a); alert(JSON.stringify(a))。你应该可靠地得到{"0":1,"length":1} - Mike Samuel
@MikeSamuel 先生,我承认我的错误。我从未想过在非数组对象上尝试它。不过,既然我经常在参数对象上使用 slice,这应该会想到我。有趣的发现。 - Flambino
2个回答

16

在SpiderMonkey中,数组基本上是作为jsvals的C数组实现的。这些被称为“稠密数组”。但是,如果您开始对它们执行不像数组的操作——例如像对象一样对待它们——它们的实现将更改为非常类似于对象的东西。

故事的寓意是:当您需要一个数组时,请使用数组。当您需要一个对象时,请使用对象。

哦,jsval是一种可变类型,可以用64位C类型表示任何可能的JavaScript值。


9
在V8和Carakan(以及可能的Chakra)中,所有(非主机)对象(无论是数组还是非数组)具有数组索引属性名称(如ES5中定义的)的对象,都存储为密集数组(包含某些值包装器的C数组)或稀疏数组(实现为二叉搜索树)。
这种统一的对象表示在枚举顺序上表现出来:对于一个对象,SpiderMonkey和SquirrelFish按照插入顺序给出所有属性;对于一个数组,它们通常(至少在SM中有特殊情况!)先给出数组索引,然后再按照插入顺序给出所有其他属性。V8、Carakan和Chakra总是首先给出数组索引,然后再按照插入顺序给出所有其他属性,而不考虑对象类型。

3
谢谢提供信息,但是来源是什么? - Klesun

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