javascript - 内部插槽和内部方法实际上是由JavaScript引擎实现的吗?

标签 javascript ecmascript-6 javascript-objects v8

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


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


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

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


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


让我们举个例子:

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

我哪里错了?

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

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

最佳答案

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


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


通常不会;引擎的行为就像其内部结构是这种结构一样。如果方便的话,实现的某些部分可能与规范的结构非常接近。

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


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


这与拒绝实施某些东西无关。通常,您可以采用多种不同的方式来实现功能,即采用多种不同的结构化代码和对象的方式。引擎可以自由地以其想要的任何方式来构造其代码和对象,只要所得到的可观察到的行为是指定的即可。


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


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

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

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

快速路径的一个特定示例是编译器。如果您将对象行为实现为(私有)方法,并且每次都加载并调用这些方法,那么您的实现将非常慢。现代引擎将JavaScript函数编译为字节码甚至机器码,并且该代码的行为就像您已经加载并调用具有给定行为的内部函数一样,但是(通常)它实际上不会调用任何此类函数。例如,用于array[index]访问的优化代码应该仅是一些机器指令(类型检查,边界检查,内存负载),并且不应调用涉及的[[Get]]。

另一个非常常见的示例是对象类型。规范通常使用这样的措辞:“如果对象具有[[StringData]]内部插槽,则...”;引擎通常将其替换为“如果对象的类型是我为内部表示字符串而选择的类型,则...”。同样,从外部观察不到这种区别:字符串的行为就像它们具有一个[[StringData]]内部插槽,但是(至少在V8中)它们没有这样的插槽,它们只是具有一个适当的对象类型来标识它们作为字符串,字符串类型的对象知道其字符有效负载在哪里,因此它们不需要任何特殊的插槽。

编辑:忘记提及:另请参见https://v8.dev/blog/understanding-ecmascript-part-1另一种解释方式。

关于javascript - 内部插槽和内部方法实际上是由JavaScript引擎实现的吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60119316/

相关文章:

类内的 JavaScript 变量始终未定义

javascript - 使用 Webpack 和 Babel 将 ES6 转换为 AMD

javascript - 尝试将过滤器应用于充满对象的嵌套数组

javascript - setInterval() 的动态变量名称

javascript - 如何从 JSON 中统计具体的值

reactjs - 当我们 `import { foo }` 时,为什么我们不能 `export foo` 而必须 `export { foo }` ?

javascript - VueJS/VueX 超出最大调用堆栈大小

javascript - javascript 中有没有一种好方法可以从 javascript 对象(而不是数组)中删除 Falsy 值?

javascript - d3 包布局中的 Json 格式问题

Javascript document.write 循环问题