JavaScriptVM如何实现对象属性访问?它是哈希表吗

How does JavaScript VM implements Object property access? Is it Hashtable?

本文关键字:访问 哈希表 属性 对象 何实现 实现 JavaScriptVM      更新时间:2023-09-26

JavaScript中的对象可以用作哈希表(键必须为String)它的数据结构是否与哈希表一样好?

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

更新:(1)我将HashMap更改为hashtable(2)我想大多数浏览器都实现了相同的功能,如果不是,为什么不呢?如何在ECMAScript规范中实现它有什么要求吗?

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

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

那么它是如何工作的呢"V8不使用动态查找来访问属性。相反,V8在幕后动态地创建隐藏类"-使得对属性的访问几乎与访问C++对象的属性一样快。

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

所以一般来说,在V8中访问对象的属性比Hashtable快。。

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

更多信息可以在这里找到:https://v8.dev/blog/fast-properties

你也可以在JS中阅读更多关于Hashtable的内容:(我的博客)http://simplenotions.wordpress.com/2011/07/05/javascript-hashtable/

"我想大多数浏览器实现的都是一样的,如果不是,为什么不呢?如何在ECMAScript规范中实现它有什么要求吗"

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

事实上,这是绝对正确的,事实上ECMA-262规范的实现独立性在规范的8.6.2节中有具体描述:

";这些表中的描述表明了它们在本地ECMAScript对象,除非本文档中针对特定类型的本机ECMAScripts对象另有说明主机对象可以通过任何依赖于实现的行为来支持这些内部属性,只要它与本文档中所述的特定主机对象限制一致即可"

";主机对象可以以任何方式实现这些内部方法,除非另有规定"

单词";hash";在整个ECMA-262规范中没有出现。

(原件,续)

JavaScript在InternetExplorer6.0和GoogleChrome的V8中的实现几乎没有任何共同之处,但(或多或少)都符合相同的规范

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

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

一天下来,你所需要知道的就是,它们是有效的。使用ajax,甚至在内存中,可能有更快的方法来创建大型集合的查找表。例如,请参阅John Reseig的博客中关于使用trie数据结构的有趣讨论。

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

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

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

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

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

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

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

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

相对资源:

  • JavaScript引擎基础知识:形状和内联缓存以及YouTube视频
  • V8之旅:对象表示
  • V8中的快速特性
  • JavaScript引擎隐藏类(以及为什么要记住它们)
  • 我应该在原型上设置属性的默认值以节省空间吗

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

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

显然,"策略"可以随着时间的推移而改变,这取决于属性的数量,从一个命名值数组到一个字典。

v8还考虑了类型,数字或字符串将不会以与对象(或函数,对象类型)相同的方式处理

如果我正确理解这一点,那么经常访问的属性(例如在循环中)将被缓存。

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

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

有关更多详细信息,请参阅文章,然后在谷歌申请工作:)