Дескрипторы свойств

Большая часть вашего взаимодействия со свойствами объекта, скорее всего, будет осуществляться на поверхностном уровне, включая создание литералов объекта, а также установку значений свойств и доступ к ним с помощью ключей. Однако вы можете внутренне настроить любое свойство объекта для детального контроля над тем, как эти свойства доступны, изменяются и определяются. Каждое свойство объекта имеет набор невидимых атрибутов, содержащих метаданные, связанные с этим свойством, называемые «дескрипторами свойств».

Существует два типа дескрипторов, связанных с любым свойством: дескрипторы данных и дескрипторы средств доступа . Дескриптор данных включает пары ключ-значение, которые содержат значение свойства, независимо от того, является ли это значение доступным для записи, настраиваемым или перечислимым. Дескрипторы средств доступа содержат функции, которые выполняются при установке, изменении или доступе к свойству.

Свойство Тип дескриптора Значение по умолчанию от
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]]