Большая часть вашего взаимодействия со свойствами объекта, скорее всего, будет осуществляться на поверхностном уровне, включая создание литералов объекта, а также установку значений свойств и доступ к ним с помощью ключей. Однако вы можете внутренне настроить любое свойство объекта для детального контроля над тем, как эти свойства доступны, изменяются и определяются. Каждое свойство объекта имеет набор невидимых атрибутов, содержащих метаданные, связанные с этим свойством, называемые «дескрипторами свойств».
Существует два типа дескрипторов, связанных с любым свойством: дескрипторы данных и дескрипторы средств доступа . Дескриптор данных включает пары ключ-значение, которые содержат значение свойства, независимо от того, является ли это значение доступным для записи, настраиваемым или перечислимым. Дескрипторы средств доступа содержат функции, которые выполняются при установке, изменении или доступе к свойству.
Свойство | Тип дескриптора | Значение по умолчанию от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]]