Рефлексия в Javascript: примеры использования объекта Proxy

oberset
02.10.2019

С помощью объекта Proxy у разработчиков появилась возможность получить контроль над некоторыми внутренними операциями, которые ранее были скрыты "под капотом" движка javascript. Механизм проксирования позволяет перехватить доступ к свойству объекта на чтение и запись, вызов конструктора объекта, вызов методов call и apply и многое другое.

Для создания proxy-объекта нужно вызвать конструктор Proxy и передать в него два аргумента: целевой объект и объект-перехватчик, методы которого определяют поведение прокси во время выполнения операций над ним: var proxyObject = new Proxy(target, { [название перехватчика]: функция обработчик }); Полный перечень всех доступных обработчиков описан в таблице:

Действие Название перехватчика / Функция обработчик (аргументы и возвращаемое значение)
Object.getOwnPropertyDescriptor(proxy, name) getOwnPropertyDescriptor:
function(target, name): PropertyDescriptor | undefined
Object.getOwnPropertyNames(proxy)
Object.getOwnPropertySymbols(proxy)
Object.keys(proxy)
ownKeys:
function(target): [string | symbol]
Object.defineProperty(proxy, name, pd) defineProperty:
function(target, name, propertyDescriptor): any
delete proxy.name deleteProperty:
function(target, name): boolean
Object.preventExtensions(proxy) preventExtensions:
function(target): boolean
name in proxy has:
function(target, name): boolean
proxy.name (доступ через get) get:
function(target, name, receiver): any
proxy.name = val (доступ через set) set:
function(target, name, val, receiver): boolean
proxy(...args)
proxy.apply(thisValue, args)
proxy.call(thisValue, ...args)
apply:
function(target, thisValue, args): any
new proxy(...args) construct:
function(target, args): object

Пример создания массива (ArrayLike) из объекта: class CountableObject { constructor(target) { this.target = target; Object.defineProperty(this.target, 'length', { enumerable: false, writable: true }); this.countProps(); return new Proxy(this.target, this.getTraps()); } countProps() { this.target.length = Object.keys(this.target).length; } getTraps() { return { set: this.trapSet.bind(this), deleteProperty: this.trapDeleteProperty.bind(this) } } trapSet(target, prop, ...args) { const updateCount = !(prop in target); const updated = Reflect.set(target, prop, ...args); if (updated && updateCount) { this.countProps(); } return updated; } trapDeleteProperty(target, prop) { if (prop in target) { delete target[prop]; this.countProps(); return true; } return false; } } class IterableObject { constructor(target) { this.target = new CountableObject(target); this.target[Symbol.iterator] = this.getIterator(); return this.target; } getIterator() { const target = this.target; return function* () { const props = Object.keys(target); for (let prop of props) { yield target[prop]; } } } } const list = {}; list[0] = 'a'; list[1] = 'b'; list[2] = 'c'; const iterableList = new IterableObject(list); iterableList[3] = 'd'; iterableList[4] = 'e'; iterableList[5] = 'f'; iterableList[6] = 'g'; console.log(iterableList.length); // 7 delete iterableList[3]; delete iterableList[4]; console.log(iterableList.length); // 5 // выводим значения свойств в цикле for (let val of iterableList) { console.log(val); } Этот код демонстрирует возможность использовать объект как массив: итерировать по полям объекта в цикле и получать общее количество его свойств (поле length).

Безопасный доступ к свойству

При попытке доступа к свойству несуществующего объекта javascript генерирует ошибку Uncaught TypeError: Cannot read property '[name]' of undefined. Чтобы избежать уведомления об ошибке разработчики обычно проверяют объект перед обращением к его свойству: const unknownObject; // undefined if (unknownObject) { console.log(unknownObject.name); } Все усложняется, когда нужно получить значение свойства вложенного объекта, в этом случае проверка становится длиннее: const unknownObject = { foo: null }; if (unknownObject && unknownObject.foo) { console.log(unknownObject.foo.name); } При увеличении уровня вложенности такая проверка существенно затрудняет чтение кода и становится крайне громоздкой.

С помощью объекта Proxy можно перехватить доступ к свойству целевого объекта и сделать необходимую проверку. Пример реализации безопасного доступа к свойству: const safeGet = (target) => { return new Proxy(target, { get(target, prop, receiver) { if (prop in target) { return Reflect.get(target, prop, receiver); } return this; } }); }; const obj = safeGet({ first: { second: { third: 3 } } }); console.log(obj.first.second.bar); // undefined console.log(obj.first.second.third); // 3 console.log(obj.first.second.third.foo); // undefined В этом примере мы написали "обертку", которая позволяет смело обращаться к свойствам вложенных объектов без необходимости предварительной проверки.

Другие примеры на Javascript:

Поиск и проверка значений в массиве на Javascript

Примеры поиска значений в массиве с помощью методов find, findIndex, includes, every и других.

Примеры использования Array reduce в Javascript

Метод reduce(callback, initialValue) возвращает результат функции callback, которая применяется к каждому элементу массива.

Для чего нужна функция debounce и как она работает

Функция debounce возвращает обертку, которая откладывает вызов исходной функции на определенное время. Пример собственной реализации функции debounce.

Поиск одинаковых элементов в массиве на Javascript

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

Как в Javascript получить массив уникальных значений (unique array)?

Несколько наиболее простых вариантов получения массива уникальных(неповторяющихся) значений в Javascript.
Как в PHP загрузить HTML-страницу со стороннего сервера?

Для загрузки HTML-страницы со стороннего сервера можно воспользоваться стандартными функциями для работы с файлами. В PHP для этой цели проще всего использовать функцию file_get_contents, только вместо локального пути к файлу, передать ей URL загружаемой страницы.

ScanFS — альтернативный инструмент поиска файлов и папок для Windows

ScanFs — продвинутая утилита, предназначенная для быстрого поиска файлов и папок на диске с применением различных фильтров.

Поиск и проверка значений в массиве на Javascript

Примеры поиска значений в массиве с помощью методов find, findIndex, includes, every и других.

Зачем нужен реверс-инжиниринг?

Реверс-инжиниринг или обратная разработка – это процесс преобразования готовой откомпилированной программы в первозданное состояние, то есть в исходный код.

Комментарии (0)

Зарегистрируйтесь или авторизуйтесь, чтобы добавлять оценки и комментарии.