我发布这个答案只是为了赞美BoltClock's answer,通过直接观看flash播放器源代码提供了一些额外的见解。我们实际上可以看到专门提供此功能的AVM代码,并且它是用C++编写的。我们可以在里面看到ArrayObject.cpp下面的代码:
// Iterator support - for in, for each
Atom ArrayObject::nextName(int index)
{
AvmAssert(index > 0);
int denseLength = (int)getDenseLength();
if (index <= denseLength)
{
AvmCore *core = this->core();
return core->intToAtom(index-1);
}
else
{
return ScriptObject::nextName (index - denseLength);
}
}
正如你可以看到,当有一个合法财产(对象)返回,它是从ScriptObject
类抬头,特别是nextName()
方法。如果我们看一下内ScriptObject.cpp这些方法:
Atom ScriptObject::nextName(int index)
{
AvmAssert(traits()->needsHashtable());
AvmAssert(index > 0);
InlineHashtable *ht = getTable();
if (uint32_t(index)-1 >= ht->getCapacity()/2)
return nullStringAtom;
const Atom* atoms = ht->getAtoms();
Atom m = ht->removeDontEnumMask(atoms[(index-1)<<1]);
if (AvmCore::isNullOrUndefined(m))
return nullStringAtom;
return m;
}
我们可以看到,确实如人在此间指出,虚拟机使用哈希表。然而,在这些函数中提供了一个特定的索引,乍一看,这就意味着必须有特定的顺序。
如果你深入挖掘(我不会在这里发布所有的代码),有不同类别的方法涉及for/for每个功能,其中一个方法是ScriptObject::nextNameIndex()
,它基本上拉起只要下一个值指向一个有效的对象,整个散列表就开始向表中的有效对象提供索引并增加参数中提供的原始索引。如果我对我的解释是正确的,这将是你随机查找的原因,我不相信在这里有任何方法来强制这些操作中的标准化/有序地图。
来源
对于那些谁可能想获取Flash播放器的开源部分的源代码,你可以从下面的善变库抓住它(你可以像github上压缩下载snapshop所以你不必安装水银):
http://hg.mozilla.org/tamarin-central - 这是“稳定”或“释放”库
http://hg.mozilla.org/tamarin-redux - 这是开发分支。 AVM的最新变化将在这里找到。这包括对Android等的支持。 Adobe仍在更新和开放Flash播放器的这些部分,所以这是当前和官方的好东西。
虽然我在这里,但这也可能是有趣的:http://code.google.com/p/redtamarin/。它是AVM的一个分支(而且相当成熟)的版本,可以用来编写服务器端的动作脚本。整齐的东西,并有大量的信息,洞察AVM的运作,所以我想我也会收录它。
我认为上面声明的对象的方式,就像一些其他语言中看到的散列表一样,您可以永远不会相信订单,因为它在内部处理自己的方式。一般的想法可以在这里找到:http://en.wikipedia.org/wiki/Hash_table – ToddBFisher 2012-04-10 04:33:41
好问题。虽然我不知道为什么迭代对象属性看起来是随机的,我回忆过去的线程讨论类似的问题,解决方案是创建一个字典,传递给按字典或键值排序的数组。我也有兴趣知道这里发生了什么。 – TheDarkIn1978 2012-04-10 05:26:33
我认为@ToddBFisher在这里有正确的想法。在C++中,这基本上是一个“无序映射”,具体来说就是TR1版本中实现的新C++ 11标准中的std :: unordered_map。请参阅http://en.wikipedia.org/wiki/Unordered_associative_containers_(C%2B%2B)和http://www.cplusplus.com/reference/stl/map/了解更多关于工作概念的细节。 – 2012-04-10 06:24:03