JavaScript VM 如何实现 Object 属性访问?它是哈希表吗?

IT技术 javascript browser
2021-01-14 10:59:42

JavaScript 中的对象可以作为 Hashtable 使用(key 必须是 String) 是不是像 Hashtable 这种数据结构表现的好?

我的意思是,它是否在幕后实现为 Hashtable?

更新:(1)我将 HashMap 更改为哈希表(2)我猜大多数浏览器都实现了它,如果不是为什么不呢?是否有任何要求如何在 ECMAScript 规范中实现它?

更新 2:我明白了,我只是想知道 V8 和 Firefox JS VM 是如何实现 Object.properties getter/setter 的?

4个回答

V8 没有实现对象属性访问作为哈希表,它实际上以更好的方式实现它(性能方面)

那么它是怎样工作的?“V8 不使用动态查找来访问属性。相反,V8 在幕后动态创建隐藏类”——这使得访问属性几乎与访问 C++ 对象的属性一样快。

为什么?因为在固定类中,每个属性都可以在特定的固定偏移位置上找到..

所以通常在 V8 中访问对象的属性比 Hashtable 更快。

我不确定它在其他虚拟机上是如何工作的

更多信息可以在这里找到:https : //developers.google.com/v8/design#prop_access

您还可以在此处阅读有关 JS 中 Hashtable 的更多信息:(我的博客)http://simplenotions.wordpress.com/2011/07/05/javascript-hashtable/

最后一个链接失效了,我觉得这个应该是新的:v8.dev/blog/fast-properties
2021-03-23 10:59:42
看起来链接又移动了,这是当前的 url github.com/v8/v8/wiki/Design%20Elements#fast-property-access
2021-03-30 10:59:42
它怎么可能为一个可以使用运行时生成的任意键的对象创建一个静态类?当然,它不能在所有情况下都这样做。
2021-04-03 10:59:42
V8 设计文档链接已损坏。这有什么想法吗?
2021-04-07 10:59:42

“我猜大多数浏览器都实现了它,如果不是为什么不呢?是否有任何要求如何在 ECMAScript 规范中实现它?”

我不是专家,但我想不出任何理由为什么语言规范会详细说明其功能必须如何在内部实现。这种约束绝对没有任何意义,因为它不会以性能以外的任何方式影响语言的功能。

编辑- 尽管有两次投反对票,但事实上,这是绝对正确的,实际上 ECMA-262 规范的实现独立性在规范的第 8.6.2 节中有具体描述

“这些表中的描述表明它们对原生 ECMAScript 对象的行为,除非本文档中针对特定种类的原生 ECMAScript 对象另有说明。宿主对象可以支持这些具有任何依赖于实现的行为的内部属性,只要它与特定的本文档中所述的宿主对象限制

“除非另有说明,否则宿主对象可以以任何方式实现这些内部方法;”

“哈希”这个词在整个 ECMA-262 规范中都没有出现。

(原文,续)

例如,Internet Explorer 6.0 和 Google Chrome 的 V8 中 Javascscript 的实现几乎没有任何共同之处,但(或多或少)都符合相同的规范。

如果你想知道一个特定的 javascript 解释器是如何做某事的,你应该专门研究那个引擎。

哈希表是创建交叉引用的有效方式。它们不是唯一的方法。例如,一些引擎可能会优化小集合的存储(对于这些集合,哈希表的开销可能效率较低)。

归根结底,您需要知道的是,它们有效。可能有更快的方法来创建大型集合的查找表,使用 ajax,甚至在内存中。例如,请参阅John Reseig 博客中关于使用特里数据结构的这篇文章的有趣讨论

但这既不在这里也不在那里。您选择使用 this 还是原生 JS 对象,不应由有关 JS 如何实现对象的信息驱动。它应该仅由性能比较驱动:每种方法如何扩展。这是您将通过执行性能测试获得的信息,而不仅仅是了解 JS 引擎实现的一些信息。

是的,性能考虑应该由实际数据驱动。但是,了解实施策略是一种非常有用的启发式方法,而且确定起来的成本非常低:这是在基准测试中的一次很棒的初步测试。您的帖子有很多有用的信息,但 OP 的问题仍然有效。
2021-03-16 10:59:42
感谢您的反对——如果我的回答有任何不准确之处,对于阅读此问题的任何人来解释和/或提供参考资料会更有用。
2021-03-19 10:59:42
你是完全正确的,我看到了一些实际使用哈希表的 js 引擎(在 1990 年代),我实际上正在阅读 v8 的源代码以了解他们现在是如何做到这一点的。由于属性名称可以动态生成,我不知道他们如何像 C、++ 编译器那样处理这个问题,这在编译时无法解决。
2021-04-02 10:59:42
我想这是因为问题是关于实际实现的(“幕后”)。说规范不需要特定的解决方案在这里的帮助有限。
2021-04-08 10:59:42

大多数现代 JS 引擎使用非常相似的技术来加速对象属性访问。该技术基于所谓的隐藏类形状了解这种优化如何编写高效的 JS 代码很重要。

JS 对象看起来像一个字典,那么为什么不使用一个来存储属性呢?哈希表的访问复杂度为 O(1),看起来是个不错的解决方案。实际上,最早的 JS 引擎已经以这种方式实现了对象。但在静态类型语言中,如 C++ 或 Java,类实例属性访问速度快如闪电。在这样的语言中,类实例只是一段内存,每个属性都有自己的常量偏移量,因此要获取属性值,我们只需要获取实例指针并为其添加偏移量。换句话说,在编译时,像这样的表达式point.x只是被它在内存中的地址替换。

也许我们可以在 JS 中实现一些类似的技术?但是如何?我们来看一个简单的JS函数:

function getX(point) {
  return point.x;
}

如何获得point.xvalue?这里的第一个问题是我们没有描述point. 但是我们可以计算一个,这就是现代 JS 引擎所做的。大多数 JS 对象在运行时都有一个绑定到对象的形状。形状描述了对象的属性以及这些属性值的存储位置。这与类定义在 C++ 或 Java 中描述类的方式非常相似。这是一个相当大的问题,一个物体的Shape是如何计算的,这里就不赘述了。我推荐这篇文章,其中包含对一般形状的很好解释,以及这篇文章这解释了这些东西是如何在 V8 中实现的。关于形状,您应该了解的最重要的一点是,所有具有相同属性且以相同顺序添加的对象将具有相同的形状。很少有例外,例如,如果一个对象有很多经常更改的属性,或者如果您使用delete运算符删除某些对象属性,则该对象将切换到字典模式并且不会有形状。

现在,让我们假设该point对象有一个属性值数组,并且我们附加了一个形状,它描述了x这个属性数组中的值的存储位置。但是还有另一个问题——我们可以将任何对象传递给函数,甚至不需要对象具有x属性。这个问题是通过称为内联缓存的技术解决的很简单,getX()第一次执行时,它记住了点的形状和x查找的结果第二次调用该函数时,它会将点的形状与前一个进行比较。如果形状匹配不需要查找,我们可以取之前的查找结果。

主要结论是所有描述相同事物的对象都应该具有相同的形状,即它们应该具有以相同顺序添加的相同属性集。它还解释了为什么总是初始化对象属性更好,即使它们是undefined默认的,这里有一个很好的问题解释。

相关资源:

这篇文章解释了它们是如何在 V8、Node.js 和大多数版本的 Google Chrome 使用的引擎中实现的

https://v8.dev/blog/fast-properties

显然,“策略”会随着时间的推移而改变,具体取决于属性的数量,从命名值数组到字典。

v8 也考虑了类型,数字或字符串不会像对象(或函数,对象的类型)一样被处理

如果我正确理解这一点,则会缓存频繁的属性访问,例如在循环中。

v8 通过观察代码实际执行的操作以及频率来动态优化代码

v8 将识别具有相同命名属性集的对象,以相同的顺序添加(就像类构造函数所做的那样,或者重复的 JSON 位,并以相同的方式处理它们。

请参阅文章了解更多详情,然后在谷歌申请工作:)