JavaScript引擎实际上实现了内部插槽和内部方法吗?

问题描述 投票:0回答:1

我正在阅读ECMA2019(在ES6中也是如此),我发现:

ECMAScript引擎中的每个对象都与一组定义其运行时行为的内部方法。这些内部方法不是ECMAScript语言的一部分。它们由本规范仅用于说明目的。但是,每个ECMAScript实现中的对象必须按照指定的方式运行通过与之关联的内部方法。确切的方式这是由实现决定的。

[我还发现了这些堆栈溢出question1question2,它们的答案似乎并没有给我我所需要的答案。

我的问题很简单。如果JavaScript引擎决定不实现其中的一些引擎,那么它们将如何确保上述规范的声明-

但是,ECMAScript实现中的每个对象必须行为与其关联的内部方法指定的一样。

让我们举个例子:

[[[GetPrototypeOf]][[Get]][[Set]][[GetOwnProperty]]等是必不可少的内部方法。如果JavaScript引擎拒绝实现它们,它将如何实现此功能?显然,他们必须实现它,只是他们可以选择具有不同的方法名称和方法签名,因为它们不是规范对其强制执行的?

我在哪里错了?

同样也用于内部插槽?如果他们没有存储该状态的内部变量,那么当被询问时,它们将如何维护该对象的状态?

EDIT:我将添加更多详细信息以澄清我的问题。让我们以Object.getPrototypeOf()为例。这是用于内部行为[[GetPrototypeOf]]的API,并且有实现它的可能算法。问题不是实现行为的可能方法-关于是否有行为!并且仍然满足规范的总体对象行为。

javascript ecmascript-6 javascript-objects v8
1个回答
2
投票

V8开发人员在这里。我认为这个问题已经在评论中得到了解答,所以我只作总结。

JavaScript引擎实际上实现了内部插槽和内部方法吗?

通常不是;引擎仅表现为仿佛,其内部结构就是以此方式构造的。如果方便的话,实现的某些部分可能与规范的结构非常接近。

一种表达方式:您可以通过首先将规范文本忠实地转换为代码(使用您选择用于引擎的任何一种语言),来实现JavaScript引擎,然后您可以重构不可见的内部结构,可以使用任何您想要的方式(例如:内联函数,或者将它们拆分,或者将它们组织为帮助器类,或者添加快速路径或缓存,或者通常将代码内翻等)。实际上,这并不奇怪:只要可观察的行为保持不变,任何程序都可以重构其内部结构。 ECMAScript当时明确指出的只是,“内部插槽”确实可以保证始终在内部且不可观察。

[[[Get]]等]是必不可少的内部方法。如果JavaScript引擎拒绝实现它们,它将如何实现此功能?

这不是拒绝执行某些操作。通常,您可以通过许多不同的方式来实现functionity,即通过许多不同的方式来构造代码和对象。引擎可以自由地以其想要的任何方式来构造其代码和对象,只要所得到的可观察到的行为是指定的即可。

让我们以Object.getPrototypeOf()为例。这是用于内部行为[[GetPrototypeOf]]

的API

不完全是。 Object.getPrototypeOf是一个公共函数,指定为以某种方式运行。规范描述的方式是必须*表现得好像有一个内部插槽[[GetPrototypeOf]]

您似乎很难想象出另一种方法。好吧,在许多情况下,引擎可能会选择具有非常接近于具有这些内部插槽的实现-可能映射到C ++类中的字段和方法。但这不是必须的。例如,可以使用自由函数代替类方法:GetPrototypeImpl(internal::Object object)而不是internal::Object::GetPrototypeImpl()。或代替继承/层次结构,引擎可以对类型使用开关语句。

引擎的实现偏离规范内部插槽定义的结构的最常见方式之一是具有其他快速路径。通常,快速路径会进行一些检查以查看其是否适用,然后再进行简单的常见案例处理。如果适用性检查失败,它将退回到较慢,更完整的实现方式,这可能更接近规范的结构。或者,也许两个函数本身都不包含完整的规范行为:您可以让GetPrototypeFromRegularObjectGetPrototypeFromProxy加上向右一个调度程序的包装程序,而所有这些[[together的行为就像规范的假设系统一样代理和常规对象上都有一个[[GetPrototypeOf]]插槽。所有这些都完全可以,因为从外面看不到行为上的差异-您只能看到Object.getPrototypeOf

快速路径的一个特定示例是编译器。如果您将对象行为实现为(私有)方法,并且每次都加载并调用这些方法,那么您的实现将非常慢。现代引擎将JavaScript函数编译为字节码甚至机器码,并且该代码

将表现为您已经加载并调用了具有给定行为的内部函数

,但(通常)实际上不会调用任何此类函数。例如,用于array[index]访问的优化代码应该仅是一些机器指令(类型检查,边界检查,内存负载),不应调用涉及的[[Get]]。另一个非常常见的示例是对象类型。规范通常使用这样的措辞:“如果对象具有[[StringData]]内部插槽,则...”;引擎通常将其替换为“如果对象的类型是我为内部表示字符串而选择的类型,则...”。同样,从外部观察不到这种差异:字符串的行为

好像它们具有

[[StringData]]内部插槽,但是(至少在V8中)它们没有这样的插槽,它们只是具有一个适当的对象可以将它们识别为字符串的类型,并且具有字符串类型的对象知道其字符有效负载在哪里,因此它们不需要任何特殊的插槽。编辑:忘记提及:另请参见https://v8.dev/blog/understanding-ecmascript-part-1另一种解释方式。
© www.soinside.com 2019 - 2024. All rights reserved.