Дескрипторы свойств объекта в JavaScript

Понятие дескриптора свойства объекта

Объект и его свойства (поля)

Объект — это набор свойств и их значений в памяти, на которые можно сослаться с помощью идентификатора.

  • ключ — это идентификатор (или имя) свойства (тип String или Symbol),
  • значения могут быть любыми (любой тип, включая другие объекты).

Свойства объекта также иногда называют полями объекта.

Курс по JavaScript купить со скидкой

Значения свойств могут иметь любой тип, включая другие объекты, что позволяет строить сложные, разветвлённые иерархии данных.

[свернуть]

Дескриптор (флаг) — это объект, позволяющий описать поведение свойства при выполнении определённых операций над ним (например, чтения или записи).

Дескрипторы свойств, присутствующие в объектах, бывают двух основных типов:

  1. дескрипторы данных (для свойства, имеющего значение, которое может (или не может) быть записываемым);
  2. дескрипторы доступа (свойство, описываемое парой функций — геттером (get) и сеттером (set).

Дескриптор может быть только одним из этих двух типов; он не может быть одновременно обоими.

Дескрипторы свойств могут содержать следующие ключи:

  1. value — значение, ассоциированное со свойством; может быть любым допустимым значением JavaScript (числом, объектом, функцией и т.д.) (по умолчанию  undefined);
  2. writable — равен true только в том случае, если значение, ассоциированное со свойством, может быть изменено с помощью оператора присваивания (по умолчанию установлено в false — перезаписать значение нельзя);
  3. enumerable — равен true только в том случае, если это свойство доступно через перечисление свойств содержащего его объекта (по умолчанию установлено в false; при false свойство будет отсутствовать в перечне перечисляемых свойств объекта);
  4. configurable — равен true только в том случае, если тип свойства может быть изменен и если свойство может быть удалено из содержащего его объекта (по умолчанию — false);
  5. get — функция, используемая как геттер свойства, либо undefined, если свойство не имеет геттера; возвращаемое значение функции будет использоваться как значение свойства (по умолчанию undefined);
  6. set — функция, используемая как сеттер свойства, либо undefined, если свойство не имеет сеттера; функция принимает единственным аргументом новое значение, присваиваемое свойству (по умолчанию установлено в undefined).

ВАЖНО! Чтобы избежать конфликтов запрещено одновременно указывать значение value и функции get/set  (указывают либо значение (value), либо функции для его чтения-записи (get или set)). Также запрещено и не имеет смысла указывать writable при наличии get/set-функций.

Примеры использования дескрипторов

Дескриптор Writable:

Дескриптор Configurable:

Дескриптор configurable определяет:

  1. можно ли изменить тип свойства enumerable или configurable с помощью функции Object.defineProperty();
  2. может ли свойство  быть удалено из содержащего его объекта.

Значение дескриптора value будет перезаписано только в том случае, если значения для остальных дескрипторов полностью совпадают с предыдущим присваиванием:

Дескриптор Enumerable:

Дескриптор enumerable определяет:

  1. будет ли свойство перебираться с помощью цикла for .. in;
  2. можно ли получить название свойства с помощью функции Object.keys.

[свернуть]

Использование дескрипторов при работе со свойствами объекта

Получение значений дескрипторов для свойств объекта:

Получение дескрипторов одного свойства ( метод Object.getOwnPropertyDescriptor() )

Метод Object.getOwnPropertyDescriptor() возвращает дескриптор собственного свойства  объекта, переданного в метод (собственное свойство объекта находится непосредственно в объекте, а не получено через цепочку прототипов.

где obj — объект, в котором ищется свойство; prop — имя свойства, чьё описание (дескрипторы) будет возвращено.

Метод Object.getOwnPropertyDescriptor()  возвращает дескриптор переданного свойства, если оно присутствует в объекте, либо undefined, если его там нет.

[свернуть]
Получение дескрипторов всех свойств объекта ( метод Object.getOwnPropertyDescriptors() )

Метод Object.getOwnPropertyDescriptors() возвращает все собственные дескрипторы свойств переданного объекта.

где obj — объект, для которого нужно получить все собственные дескрипторы свойств.

Метод Object.getOwnPropertyDescriptors() возвращает объект, содержащий все собственные дескрипторы свойств объекта. Если у переданного объекта нет свойств, то возвращается пустой объект.

[свернуть]
Примеры

[свернуть]

Добавление (изменение) свойств объекта:

Добавление (изменение) одного свойства объекта ( метод Object.defineProperty() )

Метод Object.defineProperties() определяет (добавляет) новые или изменяет существующие свойства, непосредственно в объекте, возвращая этот объект.

При вызове метода учитывается значение дескриптора configurable!

где obj — объект, в котором определяются новые или изменяются существующие свойства; props — объект, чьи собственные перечисляемые свойства представляют собой дескрипторы для создаваемых или изменяемых свойств.

Метод возвращает измененный объект.

Обратите внимание на значения по умолчанию дескрипторов configurable: false и  writable: false.

[свернуть]

Добавление (изменение) нескольких свойств объекта ( метод Object.defineProperties() )

Метод Object.defineProperties() позволяет определять все свойства переданного объекта.

При вызове метода учитывается значение дескриптора configurable!

[свернуть]

Геттеры (get) и сеттеры (set)

Дескрипторы get и set (более известные как геттеры и сеттеры) позволяют задавать функции в качестве свойств объекта, а также запускать их при запросе к вызову или записи свойства соответственно:

  • get – функция, которая возвращает значение свойства (по умолчанию undefined);
    set – функция, которая записывает значение свойства (по умолчанию undefined).

Указание get и set в в качестве свойства в литерале объекта

Если мы создаём объект при помощи литерального синтаксиса { … }, то задать свойства-функции можно прямо в его определении.

Геттер срабатывает, когда myCar.allInfo вызывается (читается), сеттер – когда значение myCar.age присваивается.

При этом мы не вызываем геттер (например, myCar.allInfo) или сеттер как функцию, а просто читаем его значение (геттер или сеттер выполняются скрыто). Снаружи свойство-аксессор выглядит как обычное свойство объекта.

Добавление get или set в существующий объект

С помощью методов Object.defineProperty() и Object.defineProperties() можно добавить get или set в уже существующий объект.

Ещё раз отметим, что свойство объекта может быть либо свойством-данным (со значением value), либо свойством-аксессором (с методами get/set).

ВАЖНО! Попытка указать и get/set, и value в одном дескрипторе вызовет ошибку:

Использование get или set в качестве обёртки

Геттеры/сеттеры можно использовать как обёртки над «реальными» значениями свойств, чтобы получить больше контроля над операциями с ними.

Например, если мы хотим запретить устанавливать короткое имя для login, мы можем использовать сеттер login для проверки, а само значение хранить в отдельном внутреннем свойстве _login:

Таким образом, login хранится в свойстве _login, доступ к которому производится через геттер и сеттер.

Замечание: технически можно получить доступ к login напрямую с помощью userLogin._login, но существует соглашение о том что свойства, которые начинаются с символа «_», являются внутренними, и к ним не следует обращаться из-за пределов объекта.

Использование get или set для совместимости кода

Аксессоры позволяют в любой момент взять «обычное» свойство и изменить его поведение, поменяв на геттер и сеттер.

Пример отсюда

Например, представим, что мы начали реализовывать объект user, используя свойства-данные имя name и возраст age:

Но вместо возраста age мы можем решить хранить дату рождения birthday, потому что так более точно и удобно:

Что делать со старым кодом, который использует свойство age?

Мы можем попытаться найти все такие места и изменить их, но это отнимает время и может быть невыполнимо, если код используется другими людьми. И кроме того, age может использоваться и в других местах кода. Давайте его сохраним.

Добавление геттера для age решит проблему:

Теперь старый код тоже работает, и у нас есть дополнительное свойство age.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Этот сайт использует Akismet для борьбы со спамом. Узнайте как обрабатываются ваши данные комментариев.