W3docs

Геттеры и сеттеры в JavaScript

Геттеры и сеттеры в JavaScript: синтаксис get/set, свойства-аксессоры, валидация, вычисляемые свойства и использование в классах.

Большинство свойств объектов в JavaScript — это свойства-данные: они просто хранят значение. Но объекты также поддерживают свойства-аксессоры: свойства, реализованные через функции, которые запускаются при чтении или записи свойства. Такие функции называются геттерами и сеттерами. Снаружи свойство-аксессор выглядит как обычное свойство (user.age), однако за кулисами именно ваш код решает, что происходит при его чтении или присвоении.

В этом руководстве рассматриваются синтаксис get/set, отличия свойств-аксессоров от свойств-данных, наиболее частые сценарии использования (валидация и вычисляемые значения), работа геттеров и сеттеров внутри классов, а также важные подводные камни.

Введение в геттеры и сеттеры свойств

Геттер — это функция, которая запускается при чтении свойства; её возвращаемое значение становится значением обращения. Сеттер — это функция, которая запускается при присвоении свойству значения; она получает присваиваемое значение единственным аргументом. Поскольку это функции, они могут выполнять валидацию, вычислять результат на лету, логировать обращения или обновлять другие свойства — и всё это при том, что вызывающий код использует обычный синтаксис свойств.

Синтаксис

Внутри литерала объекта геттер определяется ключевым словом get перед методом, а сеттер — ключевым словом set перед методом, принимающим один параметр:

let obj = {
  get propName() {
    // getter, the code executed when obj.propName is read
  },
  set propName(value) {
    // setter, the code executed when obj.propName is written
  }
};

Можно определить оба или только один. Свойство только с геттером является доступным только для чтения: присвоение ему значения в нестрогом режиме молча игнорируется, а в строгом режиме выбрасывается TypeError. Свойство только с сеттером доступно только для записи — при чтении оно возвращает undefined.

Свойства-аксессоры и свойства-данные

Стоит точно понимать, какое свойство создаёт пара геттер/сеттер. Обычное свойство вроде width: 5 является свойством-данным и имеет value. Пара геттер/сеттер, напротив, создаёт свойство-аксессор, у которого нет собственного value — только функции get и set. Эти два типа являются взаимоисключающими: один дескриптор свойства не может одновременно иметь value и get/set.

Именно поэтому в примерах с валидацией ниже настоящее число хранится в отдельном вспомогательном поле (_age): публичному аксессору age нужно где-то хранить данные, поскольку у него нет слота для значения. Подробнее об этом — в разделе Флаги и дескрипторы свойств.

Зачем использовать геттеры и сеттеры?

Геттеры и сеттеры предоставляют ряд преимуществ:

  • Инкапсуляция: они скрывают внутреннее представление за стабильным публичным интерфейсом. Впоследствии можно изменить способ хранения значения, не нарушая работу кода, который его читает.
  • Валидация: в сеттере можно отклонить или нормализовать значения до их сохранения.
  • Вычисляемые свойства: геттер может вычислять результат на основе других свойств, поэтому значение всегда актуально и не хранится в устаревшем виде.
  • Обратная совместимость: если обычное поле позднее потребует добавления логики, его можно заменить аксессором с тем же именем — вызывающий код при этом не изменится.

Практические примеры

Рассмотрим несколько практических примеров, демонстрирующих применение геттеров и сеттеров в реальных сценариях.

Пример 1: объект пользователя с валидацией возраста

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

javascript— editable

Пример 2: создание вычисляемых свойств

Геттеры позволяют создавать свойства, вычисляемые на лету на основе других данных. Каждый раз при чтении area значение пересчитывается, поэтому оно остаётся синхронизированным при изменении width или height.

javascript— editable

Пример 3: определение аксессоров с помощью Object.defineProperty

Синтаксис литерала get/set — наиболее распространённый способ объявления аксессоров, однако их можно добавить к существующему объекту — в том числе после его создания — с помощью Object.defineProperty. Это удобно, когда имя свойства является динамическим или когда нужно управлять флагами вроде enumerable.

javascript— editable

Лучшие практики

При использовании геттеров и сеттеров придерживайтесь следующих рекомендаций, чтобы ваш код был чистым, поддерживаемым и эффективным:

  • Избегайте побочных эффектов в геттерах: геттеры должны работать быстро и не иметь побочных эффектов, так как они нередко вызываются неявно движком или при перечислении свойств.
  • Валидация: всегда валидируйте данные в сеттерах, чтобы предотвратить сохранение некорректных или вредоносных данных.
  • Соглашения об именовании: используйте ведущее подчёркивание (_) для имён вспомогательных свойств, чтобы обозначить их как приватные.
  • Сериализация JSON: учитывайте, что JSON.stringify() по умолчанию игнорирует геттеры. Используйте функцию-заменитель или явную сериализацию, если нужно включить вычисляемые значения.

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

Динамические имена свойств

В JavaScript ES6 появились вычисляемые имена свойств, которые можно комбинировать с геттерами и сеттерами для динамического определения ключей аксессоров на основе переменных или выражений.

javascript— editable

Интеграция с классами

Геттеры и сеттеры также очень удобны в программировании на основе классов, обеспечивая инкапсуляцию и контроль доступа к свойствам класса. Синтаксис идентичен — достаточно написать методы get/set в теле класса — при этом они размещаются на прототипе, поэтому все экземпляры их разделяют.

javascript— editable

Распространённые ошибки

Несколько ошибок часто застают разработчиков врасплох:

  • Бесконечная рекурсия из-за неправильного вспомогательного поля. Если сеттер для name присваивает значение this.name вместо отдельного поля вроде this._name, присвоение снова вызывает сеттер, и так до бесконечности. Всегда храните значение под другим ключом.
  • Геттер без сеттера доступен только для чтения. Присвоение ему значения ничего не делает (или выбрасывает исключение в строгом режиме), что может выглядеть как молчаливая ошибка.
  • JSON.stringify() вызывает геттеры, но игнорирует сеттеры. Вычисляемый геттер включается в сериализацию (его возвращаемое значение попадает в результат), однако восстановить его через сеттер при JSON.parse() невозможно — вы получаете только простые данные.
  • this зависит от места вызова. Внутри геттера или сеттера this — это объект, через который было обращение к свойству. Если скопировать аксессор на другой объект, this изменится соответственно — см. Методы объекта и "this".

Пример ниже демонстрирует ловушку рекурсии и способ её устранения:

javascript— editable

Заключение

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

Практика

Практика
Какие утверждения верны относительно геттеров и сеттеров в JavaScript?
Какие утверждения верны относительно геттеров и сеттеров в JavaScript?
Was this page helpful?