Объектно-ориентированный JavaScript: дескрипторы
Над каждым свойством любого объекта в JavaScript можно провести определённый набор манипуляций. Свойство можно записать, изменить, получить значение, а с помощью цикла for .. in
или метода Object.keys
перечислить все свойства объекта. Вполне стандартный набор операций для работы с объектами, к которому вы, скорее всего, уже привыкли. До релиза стандарта ES5 все эти “качества” объекта изменить было невозможно, но теперь для каждого свойства можно детально описать модель его поведения с помощью дескрипторов.
Свойства или методы?
В одной из первых статей данного цикла я уже писал, что в JavaScript, на самом деле, нет никакого разделения на свойства и методы: любая пара ключ: значение является свойством, в независимости от того, на какой тип данных ссылается ключ. Другими словами, нет никакого разделения значений на функции и всё остальное — то, что обычно называют методом, просто ссылается на функцию, в результате чего свойство можно как бы вызвать. Но, если подумать о том, что происходит “за сценой”, то сразу станет понятно, что само свойство вызвать нельзя. В этом можно легко убедиться, обратившись к методу, как к обычному свойству:
Таким образом, используя метод, вы выполняете два действия: получение функции из свойства и вызов полученной функции. Понимание того, что методы являются обычными свойствами, важно в контексте изучения дескрипторов, и именно поэтому я ещё раз обратил ваше внимание на подобное поведение.
Дескрипторы
Дескрипторы позволяют описать, как будет вести себя свойство при выполнении определённых операций над ним, например, чтения или записи. Всего у каждого свойства есть шесть дескрипторов:
value
— значение свойстваwritable
— если установлен true, то значение свойства можно изменятьconfigurable
— если установлен true, то свойство можно перезаписывать с помощью новых вызововObject.defineProperty
enumerable
— если установлен true, то свойство будет перечисляться в циклеfor .. in
и при использовании методаObject.keys
get
— функция, которая будет вызвана при запросе к свойствуset
— функция, которая будет вызвана при записе свойства
Получить доступ к изменению дескрипторов можно только используя функции Object.defineProperty
, которая первым аргументом принимает объект, в который будет записано свойство, вторым — название свойства и третьим — объект содержащий необходимые дескрипторы:
Если есть необходимость задать сразу несколько свойств за раз, то следует воспользоваться функцией Object.defineProperties
:
Важно: по умолчанию дескрипторы enumerable
, configurable
и writable
установлены в значение false
, поэтому всегда стоит указывать те дескрипторы, которые вы хотите использовать.
Writable
Writable определяет возможность перезаписи значения свойства. Если установлено значение false
, то перезаписать значение нельзя.
Configurable
Дескриптор configurable определяет, можно ли перезаписать дескрипторы свойства с помощью функции Object.defineProperty
:
Свойство будет успешно перезаписано только в том случае, если значения для дескрипторов полностью совпадают с оригинальным присваиванием:
Enumerable
Каждый объект можно перебрать с помощью цикла for .. in
или же получить названия всех свойств с помощью функции Object.keys
. Дескриптор enumerable определяет, будет ли свойство перечисляться в данных ситуациях:
Геттеры и сеттеры
Два самых интересных дескриптора — get
и set
, более известные, как геттеры и сеттеры. С их помощью можно запускать обозначенные функции при запросе к получению или записи свойства соответственно.
Таким образом, можно выполнить любой код при присваивании и получении свойства, который даже может быть абсолютно не связан с изменяемым свойством, как в примере выше, где перед тем, как записать новое имя, старое добавляется к массиву.
Комментарии