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

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

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

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

Проверьте свое понимание

Какие дескрипторы являются аксессорами?

[[Set]]
[[Writable]]
[[Get]]