JavaScript引擎如何实现JavaScript对象模型以及他们使用哪些技能来速度获得JavaScript对象属性的属性?本文通过浏览器调试方法来解释上述两个问题。在写作中,本文还涉及几篇前辈的文章,该文章链接将在最后放置。
本文的主要分析结果是通过Google浏览器的快速照片获得的。操作方法如下:
eCmascript规范基本上将所有对象定义为属性属性的字典为字符串键值。
此外,规格还定义以下属性:
该符号代表一些特别的内容,但这是一种指示不能直接暴露于JavaScript的规范的定义。在JavaScript中,您仍然可以通过API获得指定对象的属性值:
在V8中,该对象主要由三个指针组成,这些指针是隐藏的类。
其中,隐藏的类用于描述对象的结构。以及用于存储对象的差异,它们的差异主要反映在是否可以索引关键名称上。
在eCmasixt规范中,数字属性应以索引值序列的顺序排列,并根据创建顺序排列字符串属性。
我们将对象中的数字属性称为排序属性,V8,字符串属性称为常规属性,即V8。
分解为这两个线性数据结构后,如果执行了索引操作,则V8将按顺序读取所有元素,然后读取属性中的所有元素,以便完成索引操作。
您为什么要介绍隐藏的课程?首先是更快的。
JavaScript是一种动态编程语言,允许开发人员以非常灵活的方式定义对象。对象可以在运行时更改类型,添加或删除属性。在相反,一旦创建类型和类型,就无法更改诸如Java的静态语言,并且可以通过固定偏移访问属性。
JS中的对象需要通过哈希表访问属性,并且需要进行其他哈希计算(稍后将提及)。为了提高对象属性的访问速度并实现对对象属性的快速访问,引入了隐藏的类在V8中。
隐藏类引入的另一个含义是极大地节省内存空间。
如前所述,对象的对象(属性)被描述为:,,。
隐藏类的引入将属性与其他属性分开。在正常情况下,对象经常发生变化,但几乎不会改变。那么为什么我们几乎不反复描述?显然,这是对记忆的浪费。
同样,多个对象具有相同的键值属性非常常见。这些对象具有相同的形状。类似地,具有相同形状对象的相同属性也很常见。
假设我们稍后会遇到相同形状的更多对象,然后在其自己的存储中包含属性名称和属性值的完整字典非常浪费(空间),因为我们重复具有相同形状的所有对象的属性名称。冗余并引入不必要的内存。作为优化,引擎将对象分别存储。
考虑到这一点,JavaScript引擎可以根据对象的形状优化对象的属性。
应该注意的是,所有JavaScript引擎都使用形状作为优化,但是它们具有不同的名称:
在创建形状创建对象的过程中,每个命名属性添加了一个新的隐藏类。连接隐藏类的转换器树在V8的底层实现。如果您以相同的顺序添加相同的属性,则转换树将确保最终获得相同的隐藏类。
我们在这里使用代码来逐步观察隐藏类中的更改:
通过上图,我们可以看到,从创建空对象到分配到对象属性,每个步骤的隐藏类都是不同的,后者隐藏的类点指向上一步的隐藏类。
进一步分析:
总而言之,我们可以粗略地推断出在实际存储中,每次添加属性时,新隐藏的类实际上只描述了此新添加的属性而不描述所有属性,即操作后的操作,即操作对象。隐藏的类实际上只描述了它。不,每个隐藏的类都将连接到先前的隐藏类。
然后,我们比较创建对象的以下两种方法:
A和B之间的区别在于,A第一个创建一个空对象,然后将名称属性添加到此对象。B直接创建一个包含命名属性的对象。我们可以从内存快速闪耀中看到。A和B的隐藏类是不同的和不同的。这主要是因为当创建隐藏的B类时,仅省略了空对象的隐藏类的步骤。因此,要生成相同的隐藏类,则更准确IS - 从相同的起点,以相同的顺序添加相同的属性(除了相同的属性)。
接下来,让我们尝试创建相同的隐藏对象。
从图中我们可以看到,尽管我们创建了两个对象,但它们的结构是相同的,因此隐藏的类是相同的。
过渡链和树在JavaScript引擎中。隐藏的表达式称为过渡链,如下图所示。
但是,如果您不能只创建一个过渡链,例如,如果您有两个空对象,并且在每个对象中添加了一个不同的属性呢?
在这种情况下,我们必须执行分支机构操作。目前,我们最终将获得一个过渡树而不是过渡链:
当然,我们不必从空对象启动创建,如下图所示:
如前所述,存储对象的属性之间的差异主要反映在是否可以索引关键名称中。索引的属性应根据索引值的顺序排列,并根据命名属性进行安排,并根据该命名属性。创建顺序。
是否应该将附件属性存储在内部?接下来,我们可以通过观察内存的快速照片来找出答案。
A和B具有命名属性。此外,A还具有两个其他有吸引力的属性属性。从快照中可以清楚地看到属性属性存储在其中。另外,A和B具有相同的结构。
在这里,我们找到了一个有趣的观点。这两个对象的属性是不同的。如何有相同的结构?
对于可可的属性,它是有序排列的,为什么我们通过其结构找到更多的结构来找到它。由于我们不需要通过其结构找到它,因此我们不需要描述它的结构不再是吗?以这种方式,不难理解为什么它们具有相同的结构,因为它们的结构只描述了它们都有这种情况。
当然,有例外。我们在上面的代码中添加另一行。
可以看出,目前隐藏的类更改,数据存储变得非常规。这是因为当我们添加时,数组将变成一个稀疏的数组。在节省空间的过程中,稀疏数组将转换为方法哈希储存,而不是使用完整的数组来描述此空间的存储。因此,属性属性不再可以通过其索引值直接计算。因为对于隐藏类的更改,它可能是更改的结构描述。
V8中的命名属性具有三种不同的存储方法:对象,快速属性和慢速属性(慢)。
让我在这里总结一下特征:
接下来,我们通过实验慢慢理解上述特征。
对象中的属性和快速属性首先查看a和b。在某种程度上,对象和快速属性的属性实际上是相同的。空间受到限制。在我的实验条件下,对象中的属性数固定为十个,并且这十个空间的大小相同(可以理解为十个指针)。当对象中的属性填充时,它将在创建顺序中存储在快速属性中,并与对象中的属性相比,快速属性需要附加额外的地址时间,然后是与对象中属性一致的线性搜索。
让我们看一下C.这是太长的时间,只有拦截的一部分。可以看出,与b(快速属性)相比,介质中的索引已成为一个不规则的数字,这意味着此对象已成为哈希沉积结构。
为什么有三种存储方法?可以这样看到。早期的JS发动机以缓慢的属性存储,并且前两个出现用于优化此存储方法。
我们知道所有数据将在底部表示为二进制。我们还知道,如果程序逻辑仅涉及二进制位操作(包括或非 - ),则速度是最快的。我们忽略了该速度的时间。解决和其他方面,并简单地比较计算数量的这三个(两个类别)方法。
在对象中完成的属性和快速属性非常简单。是否在每个位置中指定线性搜索。时间的这一部分消耗了最多ntic -bit操作的时间消耗(n是属性的总数)。慢速属性需要首先通过哈希算法来计算。这是一个复杂的操作,有几次简单操作。此外,哈希表是一个两个维空间,因此在计算哈希算法的坐标之一之后,在另一个维度上仍然需要线性搜索。因此,当属性很少时,它不应是很难理解为什么它不需要慢。
那么,为什么不总是在对象中使用属性或快速属性呢?这个问题需要我们对结构的理解。
理解后,让我们看一下V8中字符串的哈希算法。其中,有60个左右班次(60个简单的操作)。
目前,让我们回答一个问题,为什么并不总是在对象中使用属性或快速属性。
因为当属性太多时,这两种方法可能不会很慢。支持哈希操作的价格为60个简单级别的操作,而哈希算法的性能很好。如果您仅在对象中使用属性或快速属性,则当我需要访问第120个属性时,我需要120个简单的操作。对于慢速属性,我们需要进行海滩计算(60个简单操作)+第二维线性比较(少于60次,并且少于60次,并且哈希算法具有良好的性能。
在了解隐藏类别和对象存储的逻辑之后,让我们看一下删除操作对隐藏类和存储逻辑的影响。
根据我们以前的测试,A和B本身是内部属性。从快照中可以看到,删除后,A变成了缓慢的属性并返回Hash存储。
但是,如果我们按添加属性的顺序删除属性,则情况将有所不同。
我们根据相同属性将相同的属性添加到A和B中,将附加属性添加到A,然后删除此属性。可以发现,目前,A和B的隐藏类是相同的,并且没有返回哈希存储。
本文主要探讨JS对象上V8引擎的实现和优化。实际上,我们从未提到过另一件事,即继承。当然,如果您谈论继承,您将不可避免地提及一些抽象概念,例如函数,原型,__loto__和其他抽象和困难的概念。我的下一篇文章还希望通过本文的方式观察如何从V8引擎的角度观察到如何观察。
然后,请参阅下一篇文章。
图形V8
V8如何运行 - V8中的对象表示
JavaScript引擎基金会:形状和内联缓存
哈希图
JavaScript对象模型执行模型