JavaScript 引擎实际上实现了内部插槽和内部方法吗?
Are internal slot and internal methods actually implemented by JavaScript engines?
我正在阅读ECMA2019(在ES6中也是如此),在那里我发现:
Each object in an ECMAScript engine is associated with a set of
internal methods that defines its runtime behaviour. These internal
methods are not part of the ECMAScript language. They are defined by
this specification purely for expository purposes. However, each
object within an implementation of ECMAScript must behave as specified
by the internal methods associated with it. The exact manner in which
this is accomplished is determined by the implementation.
我还发现了这些 Stack Overflow question1 and ,他们的回答似乎没有给我想要的答案。
我的问题很简单。如果 JavaScript 引擎决定不实施其中的一些,那么他们将如何确保以上规范的声明 -
However, each object within an implementation of ECMAScript must
behave as specified by the internal methods associated with it.
举个例子:
[[GetPrototypeOf]]
, [[Get]]
, [[Set]]
, [[GetOwnProperty]]
等是必不可少的内部方法。如果 JavaScript 引擎拒绝实现它们,它如何实现此功能?显然他们必须实施它,只是他们可以选择具有不同的方法名称和不同的方法签名,因为它不是由规范强制执行的吗?
我哪里错了?
同样适用于内部插槽?如果他们没有存储该状态的内部变量,那么当被问及时他们将如何维护该对象的状态?
EDIT :我会添加更多细节来澄清我的问题。让我们以Object.getPrototypeOf()
为例。这是一个针对内部行为 [[GetPrototypeOf]]
的 API 并且有可能的算法来实现它。问题不是将其实现为一种行为的可能方法——它是否具有某种行为!并且仍然满足规范的整体对象行为。
这里是 V8 开发人员。我觉得这个问题大部分已经在评论里回答过了,所以我就总结一下吧。
Are internal slot and internal methods actually implemented by JavaScript engines?
一般不会;引擎的行为 就好像 它的内部结构就是这样构造的。如果方便的话,实现的某些部分可能非常接近规范的结构。
一种表达方式是:您可以通过首先忠实地将规范文本翻译成代码(无论您选择用于引擎的哪种语言)来实现 JavaScript 引擎,然后你将被允许以任何你想要的方式重构不可见的内部结构(例如:内联函数,或将它们拆分,或将它们组织为助手class,或添加快速路径或缓存,或者通常将代码翻转过来,等等)。这并不奇怪,真的:只要可观察到的行为保持不变,任何程序都可以重构其内部结构。 ECMAScript 在这一点上明确表示 "internal slots" 确实保证始终是内部的并且不可观察。
[[[Get]]
etc] are essential internal methods. If a JavaScript engine refuses to implement them, how does it achieve this functionality?
这不是拒绝实施某事。您通常可以用许多不同的方式实现 功能 ,即用许多不同的方式来构建代码和对象。引擎可以自由地以他们想要的任何方式构建他们的代码和对象,只要所产生的可观察行为是指定的。
Let us take an example of Object.getPrototypeOf(). This is an API for internal behaviour [[GetPrototypeOf]]
不完全是。 Object.getPrototypeOf
是一个 public 函数,指定以特定方式运行。规范描述它的方式是它必须 *表现得好像有一个内部插槽 [[GetPrototypeOf]]
.
您似乎很难想出替代方法。好吧,在许多情况下,引擎可能会选择一个非常接近那些内部槽的实现——可能映射到 C++ class 中的字段和方法。但这不一定是那样。例如,可以使用自由函数代替 class 方法:GetPrototypeImpl(internal::Object object)
而不是 internal::Object::GetPrototypeImpl()
。或者代替 inheritance/hierarchy 结构,引擎可以在类型上使用 switch 语句。
引擎的实现偏离规范的内部槽定义的结构的最常见方式之一是具有额外的快速路径。通常,快速路径会执行一些检查以查看它是否适用,然后执行简单、常见的情况;如果适用性检查失败,它会退回到更慢、更完整的实现,这可能更接近规范的结构。或者也许这两个函数本身都不包含完整的规范行为:你可以有 GetPrototypeFromRegularObject
和 GetPrototypeFromProxy
加上一个包装器调度到正确的,所有这些都 在一起 表现得像规范的假设系统,在代理和常规对象上都有一个 [[GetPrototypeOf]]
插槽。所有这一切都很好,因为从外部看不出行为上的差异——你所能看到的只是 Object.getPrototypeOf
.
快速路径的一个具体示例是编译器。如果您将对象行为实现为(私有)方法,并且每次都加载和调用这些方法,那么您的实现将非常缓慢。现代引擎将 JavaScript 函数编译为字节码甚至机器码,并且该代码 的行为就像您已经加载并调用具有给定行为的内部函数一样 ,但它(通常) 实际上不会调用任何此类函数。例如,array[index]
访问的优化代码应该只有几条机器指令(类型检查、边界检查、内存加载),不应调用 [[Get]]。
另一个非常常见的例子是对象类型。该规范通常使用像 "if the object has a [[StringData]] internal slot, then ..." 这样的措辞;引擎通常将其替换为 "if the object's type is what I've chosen for representing strings internally, then ..."。同样,从外部无法观察到差异:字符串的行为 就好像它们有 一个 [[StringData]]
内部槽,但是(至少在 V8 中)它们没有这样的槽,它们只是有一个适当的对象类型,将它们标识为字符串,并且具有字符串类型的对象知道它们的字符有效负载在哪里,它们不需要任何特殊的槽。
编辑:忘记提及:另请参阅 https://v8.dev/blog/understanding-ecmascript-part-1 以了解另一种解释方式。
我正在阅读ECMA2019(在ES6中也是如此),在那里我发现:
Each object in an ECMAScript engine is associated with a set of internal methods that defines its runtime behaviour. These internal methods are not part of the ECMAScript language. They are defined by this specification purely for expository purposes. However, each object within an implementation of ECMAScript must behave as specified by the internal methods associated with it. The exact manner in which this is accomplished is determined by the implementation.
我还发现了这些 Stack Overflow question1 and
我的问题很简单。如果 JavaScript 引擎决定不实施其中的一些,那么他们将如何确保以上规范的声明 -
However, each object within an implementation of ECMAScript must behave as specified by the internal methods associated with it.
举个例子:
[[GetPrototypeOf]]
, [[Get]]
, [[Set]]
, [[GetOwnProperty]]
等是必不可少的内部方法。如果 JavaScript 引擎拒绝实现它们,它如何实现此功能?显然他们必须实施它,只是他们可以选择具有不同的方法名称和不同的方法签名,因为它不是由规范强制执行的吗?
我哪里错了?
同样适用于内部插槽?如果他们没有存储该状态的内部变量,那么当被问及时他们将如何维护该对象的状态?
EDIT :我会添加更多细节来澄清我的问题。让我们以Object.getPrototypeOf()
为例。这是一个针对内部行为 [[GetPrototypeOf]]
的 API 并且有可能的算法来实现它。问题不是将其实现为一种行为的可能方法——它是否具有某种行为!并且仍然满足规范的整体对象行为。
这里是 V8 开发人员。我觉得这个问题大部分已经在评论里回答过了,所以我就总结一下吧。
Are internal slot and internal methods actually implemented by JavaScript engines?
一般不会;引擎的行为 就好像 它的内部结构就是这样构造的。如果方便的话,实现的某些部分可能非常接近规范的结构。
一种表达方式是:您可以通过首先忠实地将规范文本翻译成代码(无论您选择用于引擎的哪种语言)来实现 JavaScript 引擎,然后你将被允许以任何你想要的方式重构不可见的内部结构(例如:内联函数,或将它们拆分,或将它们组织为助手class,或添加快速路径或缓存,或者通常将代码翻转过来,等等)。这并不奇怪,真的:只要可观察到的行为保持不变,任何程序都可以重构其内部结构。 ECMAScript 在这一点上明确表示 "internal slots" 确实保证始终是内部的并且不可观察。
[
[[Get]]
etc] are essential internal methods. If a JavaScript engine refuses to implement them, how does it achieve this functionality?
这不是拒绝实施某事。您通常可以用许多不同的方式实现 功能 ,即用许多不同的方式来构建代码和对象。引擎可以自由地以他们想要的任何方式构建他们的代码和对象,只要所产生的可观察行为是指定的。
Let us take an example of Object.getPrototypeOf(). This is an API for internal behaviour [[GetPrototypeOf]]
不完全是。 Object.getPrototypeOf
是一个 public 函数,指定以特定方式运行。规范描述它的方式是它必须 *表现得好像有一个内部插槽 [[GetPrototypeOf]]
.
您似乎很难想出替代方法。好吧,在许多情况下,引擎可能会选择一个非常接近那些内部槽的实现——可能映射到 C++ class 中的字段和方法。但这不一定是那样。例如,可以使用自由函数代替 class 方法:GetPrototypeImpl(internal::Object object)
而不是 internal::Object::GetPrototypeImpl()
。或者代替 inheritance/hierarchy 结构,引擎可以在类型上使用 switch 语句。
引擎的实现偏离规范的内部槽定义的结构的最常见方式之一是具有额外的快速路径。通常,快速路径会执行一些检查以查看它是否适用,然后执行简单、常见的情况;如果适用性检查失败,它会退回到更慢、更完整的实现,这可能更接近规范的结构。或者也许这两个函数本身都不包含完整的规范行为:你可以有 GetPrototypeFromRegularObject
和 GetPrototypeFromProxy
加上一个包装器调度到正确的,所有这些都 在一起 表现得像规范的假设系统,在代理和常规对象上都有一个 [[GetPrototypeOf]]
插槽。所有这一切都很好,因为从外部看不出行为上的差异——你所能看到的只是 Object.getPrototypeOf
.
快速路径的一个具体示例是编译器。如果您将对象行为实现为(私有)方法,并且每次都加载和调用这些方法,那么您的实现将非常缓慢。现代引擎将 JavaScript 函数编译为字节码甚至机器码,并且该代码 的行为就像您已经加载并调用具有给定行为的内部函数一样 ,但它(通常) 实际上不会调用任何此类函数。例如,array[index]
访问的优化代码应该只有几条机器指令(类型检查、边界检查、内存加载),不应调用 [[Get]]。
另一个非常常见的例子是对象类型。该规范通常使用像 "if the object has a [[StringData]] internal slot, then ..." 这样的措辞;引擎通常将其替换为 "if the object's type is what I've chosen for representing strings internally, then ..."。同样,从外部无法观察到差异:字符串的行为 就好像它们有 一个 [[StringData]]
内部槽,但是(至少在 V8 中)它们没有这样的槽,它们只是有一个适当的对象类型,将它们标识为字符串,并且具有字符串类型的对象知道它们的字符有效负载在哪里,它们不需要任何特殊的槽。
编辑:忘记提及:另请参阅 https://v8.dev/blog/understanding-ecmascript-part-1 以了解另一种解释方式。