Большая часть вашего взаимодействия со свойствами объекта, скорее всего, будет осуществляться на поверхностном уровне, включая создание литералов объекта, а также установку значений свойств и доступ к ним с помощью ключей. Однако вы можете внутренне настроить любое свойство объекта для детального контроля над тем, как эти свойства доступны, изменяются и определяются. Каждое свойство объекта имеет набор невидимых атрибутов, содержащих метаданные, связанные с этим свойством, называемые «дескрипторами свойств».
Существует два типа дескрипторов, связанных с любым свойством: дескрипторы данных и дескрипторы средств доступа . Дескриптор данных включает пары ключ-значение, которые содержат значение свойства, независимо от того, является ли это значение доступным для записи, настраиваемым или перечислимым. Дескрипторы средств доступа содержат функции, которые выполняются при установке, изменении или доступе к свойству.
Свойство | Тип дескриптора | Значение по умолчанию отObject.defineProperty() | Описание |
---|---|---|---|
[[Value]] | Данные | undefined | Содержит значение свойства. |
[[Writable]] | Данные | false | Определяет, можете ли вы изменить значение свойства. |
[[Get]] | Аксессор | undefined | Функция получения свойства, которая выполняется при доступе к свойству. |
[[Set]] | Аксессор | undefined | Функция установки свойства, которая выполняется, когда свойство устанавливается или изменяется. |
[[Configurable]] | Оба | false | Если это значение false , свойство нельзя удалить и его атрибуты нельзя изменить. Если это значение false и [[Writable]] имеет true , значение свойства все равно можно изменить. |
[[Enumerable]] | Оба | false | Если это true , вы можете перебирать свойство, используя циклы for...in или статический метод Object.keys() . |
Каждое из этих свойств использует то же сокращение, что и [[Prototype]]
, что указывает на то, что эти свойства не предназначены для прямого доступа. Вместо этого используйте статический метод Object.defineProperty()
для определения или изменения свойств объекта. Object.defineProperty()
принимает три аргумента: объект, над которым нужно действовать, ключ свойства, который нужно создать или изменить, и объект, содержащий дескрипторы, связанные с создаваемым или изменяемым свойством.
По умолчанию свойства, созданные с помощью Object.defineProperty()
недоступны для записи, перечисления или настройки. Однако любое свойство, которое вы создаете либо как часть литерала объекта, либо с использованием записи через точку или скобку, доступно для записи, перечисления и настройки.
const myObj = {}; Object.defineProperty(myObj, 'myProperty', { value: true, writable: false }); myObj.myProperty; > true myObj.myProperty = false; myObj.myProperty; > true
Например, когда [[Writable]]
имеет false
значение, попытка установить новое значение для связанного свойства терпит неудачу вне строгого режима и выдает ошибку в строгом режиме :
{ const myObj = {}; Object.defineProperty(myObj, 'myProperty', { value: true, writable: false }); myObj.myProperty = false; myObj.myProperty; } > true (function () { "use strict"; const myObj = {}; Object.defineProperty(myObj, 'myProperty', { value: true, writable: false }); myObj.myProperty = false; myObj.myProperty; }());\ > Uncaught TypeError: "myProperty" is read-only
Эффективное использование дескрипторов — довольно сложная концепция, но понимание внутренней структуры объекта необходимо для понимания синтаксиса, используемого при работе с объектами более распространенными способами. Например, эти концепции вступают в силу при использовании статического метода Object.create()
, который дает вам детальный контроль над любыми прототипами, прикрепленными к новому объекту.
Object.create()
создает новый объект, используя существующий объект в качестве прототипа. Это позволяет новому объекту наследовать свойства и методы от другого пользовательского объекта так же, как объекты наследуют свойства от встроенного в JavaScript прототипа Object
. Когда вы вызываете Object.create()
с объектом в качестве аргумента, он создает пустой объект с переданным объектом в качестве его прототипа.
const myCustomPrototype = { 'myInheritedProp': 10 }; const newObject = Object.create( myCustomPrototype ); newObject; > Object { } <prototype>: Object { myInheritedProp: 10 } myInheritedProp: 10 <prototype>: Object { … }
Object.create
может принимать второй аргумент, определяющий собственные свойства для вновь созданного объекта, используя синтаксис, аналогичный Object.defineProperty()
— то есть ключи сопоставления объекта с набором атрибутов дескриптора:
const myCustomPrototype = { 'myInheritedProp': 10 }; const myObj = Object.create( myCustomPrototype, { myProperty: { value: "The new property value.", writable: true, configurable: true } }); myObj; > Object { … } myProperty: "The new property value." <prototype>: Object { myInheritedProp: 10 }
В этом примере новый объект ( myObj
) использует литерал объекта ( myCustomPrototype
) в качестве прототипа, который сам содержит унаследованный Object.prototype
, в результате чего образуется серия унаследованных прототипов, называемая цепочкой прототипов . У каждого объекта есть прототип, назначенный или унаследованный, который имеет собственный назначенный или унаследованный прототип. Эта цепочка заканчивается null
прототипом, который не имеет собственного прототипа.
const myPrototype = { 'protoProp': 10 }; const newObject = Object.setPrototypeOf( { 'objProp' : true }, myPrototype ); newObject; > Object { objProp: true } objProp: true <prototype>: Object { protoProp: 10 } protoProp: 10 <prototype>: Object { … }
Свойства, содержащиеся в прототипе значения, доступны на «верхнем уровне» объекта без необходимости прямого доступа к свойству прототипа:
const objectLiteral = { "value" : true }; objectLiteral; > Object { value: true } value: true <prototype>: Object { … } objectLiteral.toString(); "[object Object]"
Этот шаблон справедлив для всей цепочки прототипов, связанной с объектом: при попытке доступа к свойству интерпретатор ищет это свойство на каждом «уровне» цепочки прототипов сверху вниз, пока не найдет свойство или цепочку. заканчивается:
const myCustomPrototype = { 'protoProp': "Prototype property value." }; const myObj = Object.create( myCustomPrototype, { myProperty: { value: "Top-level property value.", writable: true, configurable: true } }); myObj.protoProp; > "Prototype property value."
Проверьте свое понимание
Какие дескрипторы являются аксессорами?
[[Get]]
[[Set]]
[[Writable]]