W3docs

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

Флаги свойств JavaScript (writable, enumerable, configurable) и дескрипторы: data vs accessor, defineProperty, getOwnPropertyDescriptors, freeze, seal и строгий режим.

Флаги и дескрипторы свойств JavaScript обеспечивают точный контроль над свойствами объектов, позволяя создавать надёжные и защищённые приложения. В этой статье подробно рассмотрены эти возможности с практическими примерами, которые помогут эффективно управлять поведением свойств.

Атрибуты свойств JavaScript

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

  • Writable: определяет, можно ли изменить значение свойства.
  • Enumerable: управляет видимостью свойства при перечислении, например в цикле for...in.
  • Configurable: указывает, можно ли удалить свойство или изменить его описание.

Эти флаги критически важны для управления доступом к свойствам объекта, обеспечения целостности данных и реализации инкапсуляции в JavaScript-приложениях.

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

Дескрипторы свойств содержат подробную информацию о свойстве объекта: его значение и флаги. Для их получения используется Object.getOwnPropertyDescriptor(obj, propName), а для установки — Object.defineProperty(obj, propName, descriptor). Объект дескриптора может содержать:

  • value: значение, связанное со свойством.
  • writable: указывает, можно ли изменить значение свойства.
  • enumerable: указывает, является ли свойство перечисляемым.
  • configurable: определяет, можно ли изменить дескриптор свойства и удалить свойство из объекта.

Примечание: при создании свойства обычным способом (user.name = "John") все три флага устанавливаются в true. Однако при определении нового свойства через Object.defineProperty любой неуказанный флаг по умолчанию принимает значение false.


javascript— editable

Object.getOwnPropertyDescriptor проверяет только собственные свойства объекта. Если запросить свойство, унаследованное от прототипа (или вовсе отсутствующее), метод вернёт undefined.


javascript— editable

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

Дескрипторы данных и дескрипторы доступа

До сих пор мы рассматривали дескрипторы данных, которые хранят value вместе с флагом writable. JavaScript также поддерживает дескрипторы доступа, в которых value/writable заменяются функциями-геттером и сеттером:

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

Дескриптор является либо дескриптором данных, либо дескриптором доступа — но не тем и другим одновременно. Сочетание value/writable с get/set вызывает ошибку. Оба вида дескрипторов разделяют флаги enumerable и configurable.

Свойства-аксессоры позволяют вычислять значение при чтении или проверять его при записи. Ниже показано определение аксессора fullName, основанного на двух свойствах данных:


javascript— editable

Полное описание синтаксиса get/set (включая сокращённую форму внутри литералов объектов) см. в разделе Геттеры и сеттеры свойств. Поскольку геттеры и сеттеры вызываются с this, привязанным к объекту, полезно также ознакомиться с разделом Методы объекта и «this».

Определение и чтение нескольких свойств за один шаг

Для работы с несколькими свойствами одновременно JavaScript предоставляет методы, работающие с коллекциями:

  • Object.defineProperties(obj, descriptors) определяет несколько свойств из набора дескрипторов.
  • Object.getOwnPropertyDescriptors(obj) возвращает дескрипторы всех собственных свойств (включая неперечисляемые и символьные ключи) в виде единого объекта.

Object.getOwnPropertyDescriptors особенно полезен при клонировании объекта вместе с его флагами — обычный spread-оператор или Object.assign копирует значения, но сбрасывает все флаги в true и пропускает аксессоры.


javascript— editable

Управление флагами свойств

Понимание флагов свойств и умение ими управлять — ключ к эффективной разработке на JavaScript. Рассмотрим, как контролировать эти флаги для тонкой настройки поведения свойств.

Запрет изменения свойства

Запрет на изменение свойства обеспечивает согласованность данных. Для этого нужно установить флаг writable в false.


javascript— editable

Поведение при неудачном присваивании зависит от режима выполнения кода. В нестрогом режиме запись в свойство с writable: false завершается молча: присваивание просто игнорируется, ошибка не выбрасывается, выполнение продолжается — что может скрывать ошибки. В строгом режиме ("use strict", а также по умолчанию внутри ES-модулей и тел классов) то же самое присваивание выбрасывает TypeError. Это правило применяется к любой операции, нарушающей флаг: удаление неконфигурируемого свойства или добавление свойства к нерасширяемому объекту также завершается молча в нестрогом режиме и выбрасывает ошибку в строгом.


javascript— editable

Скрытие свойства от перечисления

Иногда необходимо скрыть свойства от процессов перечисления, например от цикла for...in. Для этого нужно установить флаг enumerable в false.


javascript— editable

Запрет удаления и изменения свойства

Чтобы свойство оставалось неизменной частью объекта, установите флаг configurable в false.


javascript— editable

Установка свойства как неконфигурируемого — необратимая операция: вернуть ему конфигурируемость невозможно, и уже нельзя переключить enumerable или изменить тип дескриптора с data на accessor.

Тем не менее существуют два важных исключения для неконфигурируемого свойства:

  • Можно изменить writable с true на false (но не обратно).
  • Если свойство по-прежнему имеет writable: true, можно изменить его value — как прямым присваиванием, так и через Object.defineProperty.

Иными словами, configurable: false фиксирует форму свойства, но не обязательно его значение. Чтобы полностью заморозить значение свойства, установите одновременно configurable: false и writable: false.


javascript— editable

Высокоуровневые API на основе флагов

Устанавливать флаги по одному свойству за раз приходится редко. JavaScript предоставляет три встроенных метода, которые изменяют флаги сразу для всего объекта:

  • Object.preventExtensions(obj) — запрещает добавление новых свойств. Существующие свойства по-прежнему можно изменять и удалять.
  • Object.seal(obj) — запрещает добавление и удаление свойств, устанавливая каждому существующему свойству configurable: false. Значения ещё можно менять.
  • Object.freeze(obj) — самый строгий вариант: запечатывает объект и делает каждое свойство writable: false, поэтому ничего нельзя добавить, удалить или изменить.

Для каждого метода есть соответствующая проверка: Object.isExtensible, Object.isSealed и Object.isFrozen. Обратите внимание, что эти операции работают только на одном уровне глубины — Object.freeze не замораживает вложенные объекты (это «поверхностная» заморозка).


javascript— editable

Заключение

Флаги и дескрипторы свойств дают точный контроль над поведением свойств объектов:

  • Дескриптор данных объединяет value с writable; дескриптор доступа использует вместо них функции get/set. Оба вида разделяют флаги enumerable и configurable.
  • Читать флаги можно с помощью Object.getOwnPropertyDescriptor (одно свойство) или Object.getOwnPropertyDescriptors (все собственные свойства); устанавливать — через Object.defineProperty или Object.defineProperties. Для унаследованных и отсутствующих свойств возвращается undefined.
  • configurable: false — необратимая операция, фиксирующая форму свойства, хотя у свойства с writable: true значение всё ещё можно изменить, а сам флаг writable можно сбросить в false.
  • Нарушение флага в нестрогом режиме завершается молча, а в строгом режиме выбрасывает TypeError.
  • Используйте Object.freeze, Object.seal и Object.preventExtensions, когда нужно заблокировать весь объект, а не отдельные флаги.

Следующие шаги: изучите Геттеры и сеттеры свойств для синтаксиса аксессоров, Методы объекта и «this» — о том, как ведёт себя this внутри них, и Прототипное наследование — о том, как поиск свойств выполняется по цепочке прототипов.

Практика

Практика
Какие характеристики можно задать с помощью дескрипторов свойств в JavaScript?
Какие характеристики можно задать с помощью дескрипторов свойств в JavaScript?
Was this page helpful?