ECMAScript | Внутренние методы и внутренние слоты обычных объектов

ECMAScript | Внутренние методы и внутренние слоты обычных объектов

 

Все обычные объекты имеют внутренний слот под названием [[Prototype]]. Значение этого внутреннего слота либо null, либо объект и используется для реализации наследования. Свойства данных объекта [[Prototype]] наследуются (и отображаются как свойства дочернего объекта) в целях получения доступа, но не для установки доступа. Свойства «Разрешателя» наследуются как для получения, так и для установки доступа.

Каждый обычный объект имеет логический внутренний слот [[Extensible]], который используется для выполнения связанных с расширяемостью внутренних инвариантов методов, указанных в разделе 6.1.7.3. А именно, как только значение внутреннего слота [[Extensible]] объекта установлено в false, становится невозможным добавлять свойства к объекту, изменять значение внутреннего слота [[Prototype]] объекта или впоследствии изменить значение [[Extensible]] на true.

В следующих описаниях алгоритма предполагается, что O — обычный объект, P — значение ключа свойства, V — любое значение языка ECMAScript, а Desc — запись дескриптора свойства (Property Descriptor).

Каждый внутренний метод обычного объекта делегирует одноименную абстрактную операцию. Если такая абстрактная операция зависит от другого внутреннего метода, то внутренний метод вызывается на O вместо прямого вызова абстрактной операции с аналогичным именем. Эта семантика гарантирует, что у экзотических объектов будут вызываться переопределённые внутренние методы, когда к ним применяются внутренние методы обычного объекта.

 

Внутренние методы и абстрактные операции

 

[[GetPrototypeOf]] ( )

Внутренний метод [[GetPrototypeOf]] (Получить прототип из) обычного объекта O не принимает аргументов. При вызове он выполняет следующие шаги:

1. Вернуть ! OrdinaryGetPrototypeOf(O).

OrdinaryGetPrototypeOf ( O )

Абстрактная операция OrdinaryGetPrototypeOf (Обычное получение прототипа из) принимает аргумент O (объект). При вызове она выполняет следующие шаги:

1. Вернуть O.[[Prototype]].

 

[[SetPrototypeOf]] ( V )

Внутренний метод [[SetPrototypeOf]] (Установить прототип из) обычного объекта O принимает аргумент V (объект или ноль). При вызове он выполняет следующие шаги:

1. Вернуть ! OrdinarySetPrototypeOf(O, V).

OrdinarySetPrototypeOf ( O, V )

Абстрактная операция OrdinarySetPrototypeOf (Обычная установка прототипа из) принимает аргументы O (объект) и V (значение языка ECMAScript). При вызове она выполняет следующие шаги:

1. Утверждено: Либо Тип(V) является Object, либо Тип(V) является Null.
2. Пусть current будет O.[[Prototype]].
3. Если SameValue(V, current) является true, вернуть true.
4. Пусть расширяемость extensible будет O.[[Extensible]].
5. Если extensible является false, вернуть false.
6. Пусть p равно V.
7. Пусть сделано done будет ложью - false.
8. Повторить, пока сделано - ложно,
   a. Если p имеет значение null, установите done в значение true.
   b. Иначе, если SameValue(p, O) истинно true, вернуть false.
   c. Иначе,
      i. Если p.[[GetPrototypeOf]] не является внутренним методом обычного объекта, определенным в 10.1.1, установите для done значение true.
      ii. Иначе установите p на p.[[Prototype]].
9. Установите O.[[Prototype]] на V.
10. Вернуть истину true.
Примечание

Цикл на шаге 8 гарантирует, что не будет цикличности ни в какой цепочке прототипов, которая включает только объекты, использующие определения обычных объектов для [[GetPrototypeOf]] и [[SetPrototypeOf]].

 

[[IsExtensible]] ( )

Внутренний метод [[IsExtensible]] (Является расширяемым?) обычного объекта O не принимает аргументов. При вызове он выполняет следующие шаги:

1. Вернуть ! OrdinaryIsExtensible(O).

OrdinaryIsExtensible ( O )

Абстрактная операция OrdinaryIsExtensible (Обычный является расширяемым?) принимает аргумент O (объект). При вызове он выполняет следующие шаги:

1. Вернуть O.[[Extensible]].

 

[[PreventExtensions]] ( )

Внутренний метод [[PreventExtensions]] (Запретить расширения) обычного объекта O не принимает аргументов. При вызове он выполняет следующие шаги:

1. Вернуть ! OrdinaryPreventExtensions(O).

OrdinaryPreventExtensions ( O )

Абстрактная операция OrdinaryPreventExtensions (Обычный запрет расширения) принимает аргумент O (объект). При вызове он выполняет следующие шаги:

1. Установить O.[[Extensible]] в значение false.
2. Вернуть true.

 

[[GetOwnProperty]] ( P )

Внутренний метод [[GetOwnProperty]] (Получение собственного свойства) обычного объекта O принимает аргумент P (ключ свойства). При вызове он выполняет следующие шаги:

1. Вернуть ! OrdinaryGetOwnProperty(O, P).

OrdinaryGetOwnProperty ( O, P )

Абстрактная операция OrdinaryGetOwnProperty (Обычное получение собственного свойства) принимает аргументы O (объект) и P (ключ свойства). При вызове она выполняет следующие шаги:

1. Утверждено: IsPropertyKey(P) истинно - является true.
2. Если O не имеет собственного свойства с ключом P, вернуть undefined.
3. Пусть D будет вновь созданным дескриптором свойства без полей.
4. Пусть X будет собственным свойством O, ключ которого - P.
5. Если X - свойство данных, тогда
   a. Установите для D.[[Value]] значение атрибута X [[Value]].
   b. Установите для D.[[Writable]] значение атрибута X [[Writable]].
6. Иначе,
   a. Утверждено: X является свойством доступа.
   b. Установите для D.[[Get]] значение атрибута X [[Get]].
   c. Установите для D.[[Set]] значение атрибута X [[Set]].
7. Установите для D.[[Enumerable]] значение атрибута X [[Enumerable]].
8. Установите для D.[[Configurable]] значение атрибута X [[Configurable]].
9. Вернуть D.

 

[[DefineOwnProperty]] ( P, Desc )

Внутренний метод [[DefineOwnProperty]] (Определить собственное свойство) обычного объекта O принимает аргументы P (ключ свойства) и Desc (дескриптор свойства). При вызове он выполняет следующие шаги:

1. Вернуть ? OrdinaryDefineOwnProperty(O, P, Desc).

OrdinaryDefineOwnProperty ( O, P, Desc )

Абстрактная операция OrdinaryDefineOwnProperty (Обычное определение собственного свойства) принимает аргументы O (объект), P (ключ свойства) и Desc (дескриптор свойства). При вызове он выполняет следующие шаги:

1. Пусть current будет ? O.[[GetOwnProperty]](P).
2. Пусть extensible будет ? IsExtensible(O).
3. Вернуть ValidateAndApplyPropertyDescriptor(O, P, extensible, Desc, current).

IsCompatiblePropertyDescriptor ( Extensible, Desc, Current )

Абстрактная операция IsCompatiblePropertyDescriptor (Является совместимым дескриптор свойства?) принимает аргументы Extensible (логическое значение), Desc (дескриптор свойства) и Current (дескриптор свойства). При вызове он выполняет следующие шаги:

1. Вернуть ValidateAndApplyPropertyDescriptor(undefined, undefined, Extensible, Desc, Current).

ValidateAndApplyPropertyDescriptor ( O, P, extensible, Desc, current )

Абстрактная операция ValidateAndApplyPropertyDescriptor (Проверить и применить дескриптор свойства) принимает аргументы O (объект или undefined), P (ключ свойства), extensible (логическое значение), Desc (дескриптор свойства) и current (дескриптор свойства). При вызове он выполняет следующие шаги:

Примечание

Если undefined передается как O, выполняется только проверка, а обновления объектов не выполняются.

 

1. Утверждено: если O не является undefined, тогда IsPropertyKey(P) является true.
2. Если current является undefined, тогда
   a. Если расширяемый extensible является false, вернуть false.
   b. Утверждено: extensible является истиной - true.
   c. Если IsGenericDescriptor(Desc) истинно true или IsDataDescriptor(Desc) истинно true, то
      i. Если O не является undefined, создайте собственное свойство данных с именем P объекта O, значения атрибутов которого [[Value]], [[Writable]], [[Enumerable]] и [[Configurable]] описаны Desc. Если значение поля атрибута Desc отсутствует, для атрибута вновь созданного свойства устанавливается значение по умолчанию.
   d. Иначе,
      i. Утверждено: ! IsAccessorDescriptor(Desc) является истиной - true.
      ii. Если O не является undefined, создайте собственное свойство средства доступа с именем P объекта O, значения атрибутов которого [[Get]], [[Set]], [[Enumerable]] и [[Configurable]] описаны в Desc. Если значение поля атрибута Desc отсутствует, для атрибута вновь созданного свойства устанавливается значение по умолчанию.
   e. Верните истину true.
3. Если все поля в Desc отсутствуют, вернуть true.
4. Если current.[[Configurable]] имеет значение false, тогда
   a. Если Desc.[[Configurable]] присутствует и его значение истинно true, вернуть false.
   b. Если присутствует Desc.[[Enumerable]] и ! SameValue(Desc.[[Enumerable]], current.[[Enumerable]]) является false, вернуть false.
5. Если ! IsGenericDescriptor(Desc) истинно true, тогда
   a. ПРИМЕЧАНИЕ. Дальнейшая проверка не требуется.
6. Иначе, если ! SameValue(! IsDataDescriptor(current), ! IsDataDescriptor(Desc)) ложно false, тогда
   a. Если current.[[Configurable]] имеет значение false, вернуть false.
   b. Если IsDataDescriptor(current) истинно true, то
      i. Если O не является undefined, преобразуйте свойство с именем P объекта O из свойства данных в свойство средства доступа. Сохраните существующие значения преобразованных атрибутов [[Configurable]] и [[Enumerable]] преобразованного свойства и установите для остальных атрибутов свойства их значения по умолчанию.
   c. Иначе,
      i. Если O не является undefined, преобразуйте свойство с именем P объекта O из свойства доступа в свойство данных. Сохраните существующие значения преобразованных атрибутов [[Configurable]] и [[Enumerable]] преобразованного свойства и установите для остальных атрибутов свойства их значения по умолчанию.
7. Иначе, если IsDataDescriptor(current) и IsDataDescriptor(Desc) истинны true, тогда
   а. Если current.[[Configurable]] является false, а current.[[Writable]] является false, то
      i. Если Desc.[[Writable]] присутствует и Desc.[[Writable]] имеет значение true, вернуть false.
      ii. Если Desc.[[Value]] присутствует, а SameValue(Desc.[[Value]], current.[[Value]]) является false, вернуть false.
      iii. Верните истину true.
8. Иначе,
   a. Утверждено: ! IsAccessorDescriptor(current) и ! IsAccessorDescriptor(Desc) верны true.
   b. Если current.[[Configurable]] ложно false, то
      i. Если Desc.[[Set]] присутствует, а SameValue(Desc.[[Set]], current.[[Set]]) является false, вернуть false.
      ii. Если Desc.[[Get]] присутствует, а SameValue(Desc.[[Get]], current.[[Get]]) является false, вернуть false.
      iii. Верните истину true.
9. Если O не является undefined, тогда
   a. Для каждого поля из Desc, которое присутствует, установите соответствующий атрибут свойства с именем P объекта O равным значению поля.
10. Вернуть истину true.

 

[[HasProperty]] ( P )

Внутренний метод [[HasProperty]] (Имеет свойство) обычного объекта O принимает аргумент P (ключ свойства). При вызове он выполняет следующие шаги:

1. Вернуть ? OrdinaryHasProperty(O, P).

OrdinaryHasProperty ( O, P )

Абстрактная операция OrdinaryHasProperty (Обычный имеет свойство) принимает аргументы O (объект) и P (ключ свойства). При вызове она выполняет следующие шаги:

1. Утверждено: IsPropertyKey(P) истинно true.
2. Пусть hasOwn будет ? О.[[GetOwnProperty]](P).
3. Если hasOwn не undefined, вернуть true.
4. Пусть parent будет ? О.[[GetPrototypeOf]]().
5. Если parent не равен null, тогда
   a. Вернуть ? parent.[[HasProperty]](P).
6. Вернуть false.

 

[[Get]] ( P, Receiver )

Внутренний метод [[Get]] (Получить) обычного объекта O принимает аргументы P (ключ свойства) и Receiver (значение языка ECMAScript). При вызове он выполняет следующие шаги:

1. Вернуть ? OrdinaryGet(O, P, Receiver).

OrdinaryGet ( O, P, Receiver )

Абстрактная операция OrdinaryGet (Обычное получение) принимает аргументы O (объект), P (ключ свойства) и Receiver (значение языка ECMAScript). При вызове он выполняет следующие шаги:

1. Утверждено: IsPropertyKey(P) истинно true.
2. Пусть desc будет ? О.[[GetOwnProperty]](P).
3. Если desc является undefined, то
   a. Пусть parent будет ? О.[[GetPrototypeOf]]().
   b. Если parent равен null, вернуть значение undefined.
   c. Вернуть ? parent.[[Get]] (P, Receiver).
4. Если IsDataDescriptor(desc) истинно true, вернуть desc.[[Value]].
5. Утверждено: IsAccessorDescriptor(desc) истинно true.
6. Пусть getter будет desc.[[Get]].
7. Если getter является undefined, вернуть undefined.
8. Вернуть ? вызов Call(getter, Receiver).

 

[[Set]] ( P, V, Receiver )

Внутренний метод [[Set]] (Установить) обычного объекта O принимает аргументы P (ключ свойства), V (значение языка ECMAScript) и Receiver (значение языка ECMAScript). При вызове он выполняет следующие шаги:

1. Вернуть ? OrdinarySet(O, P, V, Receiver).

OrdinarySet ( O, P, V, Receiver )

Абстрактная операция OrdinarySet (Обычная установка) принимает аргументы O (объект), P (ключ свойства), V (значение языка ECMAScript) и Receiver (значение языка ECMAScript). При вызове она выполняет следующие шаги:

1. Утверждено: IsPropertyKey(P) истинно true.
2. Пусть ownDesc будет ? О.[[GetOwnProperty]](P).
3. Вернуть OrdinarySetWithOwnDescriptor(O, P, V, Receiver, ownDesc).

 

OrdinarySetWithOwnDescriptor ( O, P, V, Receiver, ownDesc )

Абстрактная операция OrdinarySetWithOwnDescriptor (Обычная установка с собственным дескриптором) принимает аргументы O (объект), P (ключ свойства), V (значение языка ECMAScript), Receiver (значение языка ECMAScript) и ownDesc (дескриптор свойства или undefined). При вызове она выполняет следующие шаги:

1. Утверждено: IsPropertyKey(P) истинно true.
2. Если ownDesc является undefined, то
   a. Пусть parent будет ? О.[[GetPrototypeOf]]().
   b. Если parent не равен null, тогда
      i. Вернуть ? parent.[[Set]](P, V, Receiver).
   c. Иначе,
      i. Установите ownDesc в PropertyDescriptor {[[Value]]: undefined, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true} ".
3. Если IsDataDescriptor(ownDesc) истинно true, то
   a. Если ownDesc.[[Writable]] имеет значение false, вернуть false.
   b. Если Тип(Receiver) не является Объектом, вернуть false.
   c. Пусть существующий дескриптор existingDescriptor будет ? Receiver.[[GetOwnProperty]](P).
   d. Если существующий дескриптор existingDescriptor не является undefined, тогда
      i. Если IsAccessorDescriptor(existingDescriptor) имеет значение true, вернуть false.
      ii. Если existingDescriptor.[[Writable]] ложно false, вернуть ложь false.
      iii. Пусть valueDesc будет дескриптором свойства {[[Value]]: V}.
      iv. Вернуть ? Receiver.[[DefineOwnProperty]](P, valueDesc).
   е. Иначе,
      i. Утверждено: Receiver в настоящее время не имеет свойства P.
      ii. Вернуть ? CreateDataProperty(Receiver, P, V).
4. Утверждено: IsAccessorDescriptor(ownDesc) истинно true.
5. Пусть установщик setter будет ownDesc.[[Set]].
6. Если установщик setter является undefined, вернуть false.
7. Выполнить ? вызов Call(setter, Receiver, «V»).
8. Вернуть истину true.

 

[[Delete]] ( P )

Внутренний метод [[Delete]] (Удалить) обычного объекта O принимает аргумент P (ключ свойства). При вызове он выполняет следующие шаги:

1. Вернуть ? OrdinaryDelete(O, P)

OrdinaryDelete ( O, P )

Абстрактная операция OrdinaryDelete (Обычное удаление) принимает аргументы O (объект) и P (ключ свойства). При вызове он выполняет следующие шаги:

1. Утверждено: IsPropertyKey(P) является true.
2. Пусть desc будет ? О.[[GetOwnProperty]] (P).
3. Если desc является undefined, вернуть true.
4. Если desc.[[Configurable]] является true, то
   a. Удалите собственное свойство с именем P из O.
   b. Верните true.
5. Вернуть false.

 

[[OwnPropertyKeys]] ( )

Внутренний метод [[OwnPropertyKeys]] обычного объекта O не принимает аргументов. При вызове он выполняет следующие шаги:

1. Вернуть ! OrdinaryOwnPropertyKeys(O).

10.1.11.1 OrdinaryOwnPropertyKeys ( O )

Абстрактная операция OrdinaryOwnPropertyKeys (Ключи собственного свойства обычного) принимает аргумент O (объект). При вызове он выполняет следующие шаги:

1. Пусть ключи keys будут новым пустым Списком.
2. Для каждого собственного ключа свойства P из O, такого что P является индексом массива, в порядке возрастания числовых индексов выполните
   а. Добавьте P как последний элемент ключей keys.
3. Для каждого собственного ключа свойства P из O, такого что Type(P) является String, а P не является индексом массива, в возрастающем хронологическом порядке создания свойства выполните
   а. Добавьте P как последний элемент ключей keys.
4. Для каждого собственного ключа свойства P из O, такого что Type(P) является Symbol, в возрастающем хронологическом порядке создания свойства выполните
   а. Добавьте P как последний элемент ключей keys.
5. Вернуть ключи keys.

OrdinaryObjectCreate ( proto [ , additionalInternalSlotsList ] )

Абстрактная операция OrdinaryObjectCreate (Создание обычного объекта) принимает аргумент proto (Объект или значение null) и необязательный аргумент additionalInternalSlotsList (список имен внутренних слотов). Он используется для указания создания во время выполнения новых обычных объектов. additionalInternalSlotsList содержит имена дополнительных внутренних слотов, которые должны быть определены как часть объекта, помимо [[Prototype]] и [[Extensible]]. Если additionalInternalSlotsList не указан, используется новый пустой список. При вызове он выполняет следующие шаги:

1. Пусть internalSlotsList будет «[[Prototype]], [[Extensible]]».
2. Если присутствует additionalInternalSlotsList, добавьте каждый из его элементов в internalSlotsList.
3. Пусть O будет ! MakeBasicObject(internalSlotsList).
4. Установите O.[[Prototype]] в proto.
5. Вернуть O.
Примечание

Хотя OrdinaryObjectCreate делает немного больше, чем вызывает MakeBasicObject, его использование сообщает о намерении создать обычный объект, а не экзотический. Таким образом, в рамках этой спецификации он не вызывается никаким алгоритмом, который впоследствии изменяет внутренние методы объекта таким образом, чтобы сделать результат необычным. Операции, создающие экзотические объекты, напрямую вызывают MakeBasicObject.

 

OrdinaryCreateFromConstructor ( constructor, intrinsicDefaultProto [ , internalSlotsList ] )

Абстрактная операция OrdinaryCreateFromConstructor (Создание обычного объекта из конструктора) принимает аргументы constructor и intrinsicDefaultProto, а также необязательный аргумент internalSlotsList (список имен внутренних слотов). Он создает обычный объект, значение [[Prototype]] которого извлекается из свойства «prototype» конструктора, если оно существует. В противном случае для [[Prototype]] используется внутренняя функция с именем intrinsicDefaultProto. internalSlotsList содержит имена дополнительных внутренних слотов, которые должны быть определены как часть объекта. Если internalSlotsList не указан, используется новый пустой список. При вызове он выполняет следующие шаги:

1. Утверждено: intrinsicDefaultProto - это строковое значение, которое в данной спецификации является именем внутреннего объекта. Соответствующий объект должен быть внутренним, который предназначен для использования в качестве значения [[Prototype]] объекта.
2. Пусть proto будет ? GetPrototypeFromConstructor(constructor, intrinsicDefaultProto).
3. Вернуть ! OrdinaryObjectCreate(proto, internalSlotsList).

 

GetPrototypeFromConstructor ( constructor, intrinsicDefaultProto )

Абстрактная операция GetPrototypeFromConstructor (Получить прототип из конструктора) принимает аргументы constructor и intrinsicDefaultProto. Он определяет значение [[Prototype]], которое следует использовать для создания объекта, соответствующего конкретному конструктору. Значение извлекается из свойства «prototype» конструктора, если оно существует. В противном случае для [[Prototype]] используется внутренняя функция с именем intrinsicDefaultProto. При вызове он выполняет следующие шаги:

1. Утверждено: intrinsicDefaultProto - это строковое значение, которое в данной спецификации является именем внутреннего объекта. Соответствующий объект должен быть внутренним, который предназначен для использования в качестве значения [[Prototype]] объекта.
2. Утверждено: IsCallable(конструктор) является true.
3. Пусть proto будет ? Get(constructor, "prototype").
4. Если Type(proto) не Object, то
   a. Пусть realm будет ? GetFunctionRealm(constructor).
   b. Установите proto как внутренний объект области realm с именем intrinsicDefaultProto.
5. Вернуть proto.
Примечание

Если конструктор constructor не предоставляет значение [[Prototype]], используемое значение по умолчанию получается из области функции конструктора constructor, а не из текущего контекста выполнения.

 

RequireInternalSlot ( O, internalSlot )

Абстрактная операция RequireInternalSlot (Требуется внутренний слот) принимает аргументы O и internalSlot. Она генерирует исключение, если O не является объектом и не имеет заданного внутреннего слота. При вызове она выполняет следующие шаги:

1. Если Тип(O) не является объектом, выбросить исключение TypeError.
2. Если O не имеет внутреннего слота internalSlot, выбросить исключение TypeError.

 

Информационные ссылки

Стандарт ECMAScript — Раздел «10 Ordinary and Exotic Objects Behaviours» — https://tc39.es/ecma262/#sec-ordinary-and-exotic-objects-behaviours
Стандарт ECMAScript — Раздел «10.1 Ordinary Object Internal Methods and Internal Slots» — https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots
Стандарт ECMAScript — Раздел «10.2 ECMAScript Function Objects» — https://tc39.es/ecma262/#sec-ecmascript-function-objects
Стандарт ECMAScript — Раздел «10.3 Built-in Function Objects» — https://tc39.es/ecma262/#sec-built-in-function-objects
Стандарт ECMAScript — Раздел «10.4 Built-in Exotic Object Internal Methods and Slots» — https://tc39.es/ecma262/#sec-built-in-exotic-object-internal-methods-and-slots
Стандарт ECMAScript — Раздел «10.5 Proxy Object Internal Methods and Internal Slots» — https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots