V8 разработчик здесь. Я думаю, что на этот вопрос в основном уже отвечали уже в комментариях, поэтому я просто подведу итоги.
Реализуются ли внутренние слоты и внутренние методы механизмами JavaScript?
Обычно нет; двигатель просто ведет себя , как если бы его внутренности были структурированы таким образом. Некоторые части реализации могут быть очень близки к структуре spe c, если это удобно.
Один из способов выразить это было бы так: вы могли бы реализовать механизм JavaScript, сначала добросовестно переведя spe c text to code (на любом языке, который вы решите использовать для своего движка), , и тогда вам будет разрешено реорганизовать невидимые внутренние компоненты любым удобным для вас способом (например, встроенные функции или разделите их, или организуйте их как вспомогательный класс, или добавьте быстрый путь или кэш, или вообще выверните код наизнанку и т. д. c). Что неудивительно, на самом деле: до тех пор, пока наблюдаемое поведение остается неизменным, любой программе разрешается проводить рефакторинг своих внутренних компонентов. На этом этапе ECMAScript ясно показывает, что «внутренние слоты» действительно гарантированно всегда являются внутренними и не наблюдаемыми.
[[[Get]]
и т. Д.] Являются важными внутренними методами. Если движок JavaScript отказывается их реализовывать, как он достигает этой функциональности?
Речь не идет об отказе от реализации чего-либо. Обычно вы можете реализовать функциональность различными способами, то есть с помощью различных способов структурирования вашего кода и ваших объектов. Двигатели могут свободно структурировать свой код и объекты так, как им хочется, при условии, что полученное наблюдаемое поведение соответствует указанному.
Давайте рассмотрим пример Object.getPrototypeOf (). Это API для внутреннего поведения [[GetPrototypeOf]]
Не совсем. Object.getPrototypeOf
- это публичная c функция, которая определенным образом работает. Способ, описанный в spe c, заключается в том, что он должен * вести себя так, как если бы был внутренний слот [[GetPrototypeOf]]
.
Кажется, у вас возникли проблемы с воображением альтернативного пути. Ну, во многих случаях движки, вероятно, захотят иметь реализацию, очень близкую к наличию этих внутренних слотов - возможно, сопоставленную с полями и методами в классе C ++. Но так не должно быть; например, вместо методов класса могут быть свободные функции: GetPrototypeImpl(internal::Object object)
вместо internal::Object::GetPrototypeImpl()
. Или вместо структуры наследования / иерархии движок может использовать операторы переключения над типами.
Один из наиболее распространенных способов, в которых реализации движков отклоняются от структуры, определенной внутренним значением spe c. слоты, имеющие дополнительные быстрые пути. Как правило, быстрый путь выполняет несколько проверок, чтобы выяснить, применимо ли это, а затем выполняет простой общий случай; если проверка применимости не удалась, она возвращается к более медленной, более полной реализации, которая может быть намного ближе к структуре spe c. Или, может быть, ни одна из функций сама по себе не содержит полного специфического поведения c: у вас может быть GetPrototypeFromRegularObject
и GetPrototypeFromProxy
плюс обертка, отправляющаяся в правую, и все эти вместе ведут себя как Гипотетическая система spe c имеет слот [[GetPrototypeOf]]
на прокси и обычных объектах. Все это совершенно нормально, потому что снаружи вы не видите различий в поведении - все, что вы можете видеть, это Object.getPrototypeOf
.
Один конкретный пример быстрого пути - это компилятор. Если бы вы реализовали поведение объекта как (приватные) методы, загружали и вызывали эти методы каждый раз, тогда ваша реализация будет очень медленной. Современные движки компилируют JavaScript функции для байт-кода или даже машинного кода, и этот код будет вести себя так, как если бы вы загрузили и вызвали внутреннюю функцию с заданным поведением , но она (обычно) фактически не будет вызывать какие-либо такие функции. Например, оптимизированный код для доступа array[index]
должен состоять только из нескольких машинных инструкций (проверка типа, проверка границ, загрузка памяти), не должно быть вызова задействованного [[Get]].
Другой Очень распространенный пример - типы объектов. В spe c обычно используется такая формулировка, как "если у объекта есть внутренний слот [[StringData]], то ..."; двигатель обычно заменяет это на «если тип объекта - это то, что я выбрал для внутреннего представления строк, тогда ...». Опять же, разница не наблюдается снаружи: строки ведут себя , как если бы они имели a [[StringData]]
внутренний слот, но (по крайней мере в V8) у них нет такого слота, у них просто есть соответствующий тип объекта, который идентифицирует их как строки, а объекты со строковым типом знают, где находится их символьная нагрузка, им для этого не требуется никакого специального слота.
Редактировать: забыли упомянуть: см. также https://v8.dev/blog/understanding-ecmascript-part-1 для другого способа объяснить это.