W3docs

JavaScript Proxy и Reflect

Узнайте, как работают JavaScript Proxy и Reflect: перехватывайте get, set, apply, has и delete с помощью ловушек, применяйте прокси для валидации, контроля доступа и логирования.

Прокси JavaScript позволяют перехватывать и переопределять фундаментальные операции над объектами — чтение свойства, запись, проверку существования ключа, вызов функции и многое другое. В паре с API Reflect, который выполняет те же операции «по умолчанию», прокси дают вам чистый официальный механизм добавления поведения к объектам без их непосредственного изменения.

В этой главе рассматривается, что такое прокси, наиболее распространённые ловушки (get, set, apply, has, deleteProperty), как Reflect дополняет прокси, а также несколько реальных паттернов — валидация, контроль доступа и логирование.

Что такое Proxy

Proxy оборачивает целевой object и обработчик. Обработчик — это набор функций, называемых ловушками, каждая из которых перехватывает определённую операцию. Когда вы взаимодействуете с прокси, вместо поведения по умолчанию запускается соответствующая ловушка; если для операции ловушка не определена, прокси передаёт её целевому объекту без изменений.

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

Синтаксис Proxy

const proxy = new Proxy(target, handler);
  • target: Исходный object, операции над которым вы хотите перехватывать.
  • handler: Объект, методы (ловушки) которого определяют поведение операций.

После этого вы работаете с proxy точно так же, как с исходным объектом; разница в том, что между ними выполняются ваши ловушки.

Понимание ловушки get

Ловушка get перехватывает чтение свойств целевого объекта. Она получает target, ключ property и receiver (сам прокси). Её часто используют для логирования обращений, вычисления свойств на лету или возврата значений по умолчанию для отсутствующих ключей.

Пример:

javascript— editable

Этот код создаёт прокси для логирования обращений к свойствам объекта.

  • Обработчик: Определяет ловушку get для логирования обращаемого свойства.
  • Целевой объект: Содержит свойства name и age.
  • Прокси: Оборачивает object target с помощью handler.

При обращении к proxy.name выводится «Getting name» и возвращается «John». Это полезно для мониторинга или отладки обращений к свойствам.

Управление операциями над объектами с помощью ловушек set и apply

Ловушка set

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

Пример:

javascript— editable

Этот код создаёт прокси для валидации и логирования присваиваний свойств объекта.

  • Обработчик: Определяет ловушку set для проверки допустимых значений свойства age и логирования попыток его установки.
  • Прокси: Оборачивает object target с помощью handler.

При установке proxy.age проверяется, является ли значение допустимым возрастом (0–150). Если нет — выводится ошибка и выбрасывается исключение.

Ловушка apply

Метод apply в JavaScript Proxy перехватывает вызовы функций. Он принимает три аргумента:

  1. target: Исходная вызываемая функция.
  2. thisArg: Значение this внутри функции.
  3. argumentsList: Массив аргументов, переданных функции.

Пример:

javascript— editable

Этот код создаёт прокси для логирования вызовов функции и её аргументов.

  • Обработчик: Определяет ловушку apply для логирования аргументов при вызове функции.
  • Функция: sum складывает два числа.
  • Прокси: Оборачивает функцию sum с помощью handler.

В приведённом коде ловушка apply логирует аргументы, а затем вызывает исходную функцию через target.apply(thisArg, argumentsList). Это полезно для логирования, отладки или динамического изменения поведения функции.

Ловушка has

Ловушка has перехватывает оператор in. Распространённый случай использования — скрытие «приватных» ключей (по соглашению имена, начинающиеся с _), чтобы они не были видны снаружи.

Пример:

javascript— editable

Даже если _secret по-прежнему существует в целевом объекте, оператор in возвращает false, и ключ фактически скрыт от кода, проверяющего объект.

Ловушка deleteProperty

Ловушка deleteProperty перехватывает оператор delete, позволяя защитить определённые ключи от удаления. Она должна возвращать true, когда удаление разрешено, или выбрасывать исключение для блокировки в строгом режиме.

Пример:

javascript— editable
Внимание

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

API Reflect

Reflect — это встроенный object, предоставляющий один метод для каждой перехватываемой операции — ровно тот же набор, что и у ловушек прокси (Reflect.get, Reflect.set, Reflect.has, Reflect.deleteProperty, Reflect.apply и т. д.). Каждый метод выполняет стандартную версию этой операции.

Это делает Reflect естественным дополнением к Proxy: внутри ловушки вы обычно хотите добавить какое-то поведение, а затем позволить операции продолжиться штатно. Вызов соответствующего метода Reflect делает именно это, причём он корректно передаёт receiver (что важно для геттеров/сеттеров) — в отличие от простого target[property]. Этот паттерн используется в практических примерах ниже.

Методы Reflect также возвращают значения вместо выбрасывания исключений — например, Reflect.set возвращает boolean, указывающий на успех, — что делает операции более предсказуемыми по сравнению с операторами или аналогами Object.*.

Вот краткий обзор ключевых методов Reflect:

1. Reflect.get()

Этот метод используется для получения значения свойства объекта.

Пример:

javascript— editable

2. Reflect.set()

Этот метод используется для установки значения свойства объекта.

Пример:

javascript— editable

3. Reflect.has()

Этот метод проверяет, существует ли свойство в объекте.

Пример:

javascript— editable

4. Reflect.deleteProperty()

Этот метод удаляет свойство из объекта.

Пример:

javascript— editable

5. Reflect.ownKeys()

Этот метод возвращает все собственные ключи свойств объекта.

Пример:

javascript— editable

6. Reflect.apply()

Этот метод вызывает целевую функцию с заданными аргументами.

Пример:

javascript— editable

7. Reflect.construct()

Этот метод используется для создания нового экземпляра объекта.

Пример:

javascript— editable

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

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

Пример 1: Автоматическая инициализация свойств

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

javascript— editable

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

Пример 2: Контроль доступа

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

javascript— editable

Этот код защищает объект, контролируя доступ к его свойствам. Он блокирует чтение sensitiveData и запрещает изменение свойства readOnly, помогая сохранить данные в безопасности.

Пример 3: Логирование и отладка

Описание: Прокси можно использовать для логирования взаимодействий с объектом, что помогает при отладке и мониторинге операций. В этом примере создаётся прокси, который логирует все операции get, set и вызовы методов над объектом.

javascript— editable

Этот код отслеживает каждое обращение или изменение свойства объекта, что очень помогает понять, что делает ваш код и когда.

Пример 4: Валидация данных

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

javascript— editable

Этот пример демонстрирует использование JavaScript Proxy для валидации и логирования изменений свойств. Object validator проверяет, является ли свойство age допустимым числом в диапазоне от 0 до 150. Если нет — логирует ошибку и выбрасывает исключение. В противном случае логирует новое значение и обновляет свойство. Object person использует этот валидатор для управления свойством age, гарантируя перехват и логирование недопустимых значений.

Заключение

Освоение прокси JavaScript позволяет управлять и расширять поведение объектов без их непосредственного изменения. Прокси могут применять правила валидации и контроля доступа, предоставлять значения по умолчанию и служить основой для инструментов логирования или отладки, тогда как Reflect обеспечивает чистоту и предсказуемость базовых операций. Применяя их умеренно, вы сможете создавать более динамичные и безопасные приложения.

Чтобы глубже изучить операции, перехватываемые прокси, ознакомьтесь с разделами геттеры и сеттеры свойств и флаги и дескрипторы свойств. Для паттерна валидации, показанного выше, полезными дополнениями станут обработка ошибок с try...catch и классы.

Практика

Практика
Какова основная функциональность объектов Proxy и Reflect в JavaScript?
Какова основная функциональность объектов Proxy и Reflect в JavaScript?
Was this page helpful?