Прокси (proxy) – особый объект, смысл которого – перехватывать обращения к другому объекту и, при необходимости, модифицировать их.
Синтаксис:
let proxy = new Proxy(target, handler)
Здесь:
target– объект, обращения к которому надо перехватывать.handler– объект с «ловушками»: функциями-перехватчиками для операций кtarget.
Почти любая операция может быть перехвачена и обработана прокси до или даже вместо доступа к объекту
target, например: чтение и запись свойств, получение списка свойств, вызов функции (если target – функция) и т.п.
Различных типов ловушек довольно много.
Сначала мы подробно рассмотрим самые важные «ловушки», а затем посмотрим и на их полный список.
Если ловушки нет – операция идёт над
target
Если для операции нет ловушки, то она выполняется напрямую над
target.get/set
Самыми частыми являются ловушки для чтения и записи в объект:
get(target, property, receiver)- Срабатывает при чтении свойства из прокси. Аргументы:
target– целевой объект, тот же который был передан первым аргументом вnew Proxy.property– имя свойства.receiver– объект, к которому было применено присваивание. Обычно сам прокси, либо прототипно наследующий от него. Этот аргумент используется редко.
set(target, property, value, receiver)- Срабатывает при записи свойства в прокси.Аргументы:
target– целевой объект, тот же который был передан первым аргументом вnew Proxy.property– имя свойства.value– значение свойства.receiver– объект, к которому было применено присваивание, обычно сам прокси, либо прототипно наследующий от него.Методsetдолжен вернутьtrue, если присвоение успешно обработано иfalseв случае ошибки (приведёт к генерацииTypeError).
Пример с выводом всех операций чтения и записи:
'use strict';letuser={};letproxy=newProxy(user,{get(target,prop){alert(`Чтение${prop}`);returntarget[prop];},set(target,prop,value){alert(`Запись${prop}${value}`);target[prop]=value;returntrue;}});proxy.firstName="Ilya";// записьproxy.firstName;// чтениеalert(user.firstName);// Ilya
При каждой операции чтения и записи свойств
proxy в коде выше срабатывают методы get/set. Через них значение в конечном счёте попадает в объект (или считывается из него).
Можно сделать и позаковыристее.
Методы
get/set позволяют реализовать доступ к произвольным свойствам, которых в объекте нет.
Например, в коде ниже словарь
dictionary содержит различные фразы:'use strict';
let dictionary = {
'Hello': 'Привет',
'Bye': 'Пока'
};
alert( dictionary['Hello'] ); // Привет
А что, если фразы нет? В этом случае будем возвращать фразу без перевода и, на всякий случай, писать об этом в консоль:
'use strict';letdictionary={'Hello':'Привет','Bye':'Пока'};dictionary=newProxy(dictionary,{get(target,phrase){if(phraseintarget){returntarget[phrase];}else{console.log(`No phrase:${phrase}`);returnphrase;}}})// Обращаемся к произвольным свойствам словаря!alert(dictionary['Hello']);// Приветalert(dictionary['Welcome']);// Welcome (без перевода)
Аналогично и перехватчик
set может организовать работу с произвольными свойствами.has
Ловушка
has срабатывает в операторе in и некоторых других случаях, когда проверяется наличие свойства.
В примере выше, если проверить наличие свойства
Welcome в dictionary, то оператор in вернёт false:alert( 'Hello' in dictionary ); // true
alert( 'Welcome' in dictionary ); // false, нет такого свойства
Это потому, что для перехвата
in используется ловушка has. При отсутствии ловушки операция производится напрямую над исходным объектом target, что и даёт такой результат.
Синтаксис
has аналогичен get.
Вот так
dictionary будет всегда возвращать true для любой in-проверки:'use strict';letdictionary={'Hello':'Привет'};dictionary=newProxy(dictionary,{has(target,phrase){returntrue;}});alert("BlaBlaBla"indictionary);// true
deleteProperty
Ловушка
deleteProperty по синтаксису аналогична get/has.
Срабатывает при операции
delete, должна вернуть true, если удаление было успешным.
В примере ниже
delete не повлияет на исходный объект, так как все операции перехватываются и «аннигилируются» прокси:'use strict';letdictionary={'Hello':'Привет'};letproxy=newProxy(dictionary,{deleteProperty(target,phrase){returntrue;// ничего не делаем, но возвращает true}});// не удалит свойствоdeleteproxy['Hello'];alert("Hello"indictionary);// true// будет то же самое, что и выше// так как нет ловушки has, операция in сработает на исходном объектеalert("Hello"inproxy);// true
apply
Прокси умеет работать не только с обычными объектами, но и с функциями.
Если аргумент
target прокси – функция, то становится доступна ловушка apply для её вызова.
Метод
apply(target, thisArgument, argumentsList) получает:target– исходный объект.thisArgument– контекстthisвызова.argumentsList– аргументы вызова в виде массива.
Она может обработать вызов сама и/или передать его функции.
'use strict';functionsum(a,b){returna+b;}letproxy=newProxy(sum,{// передаст вызов в target, предварительно сообщив о нёмapply:function(target,thisArg,argumentsList){alert(`Буду вычислять сумму:${argumentsList}`);returntarget.apply(thisArg,argumentsList);}});// Выведет сначала сообщение из прокси,// а затем уже суммуalert(proxy(1,2));
Нечто подобное можно сделать через замыкания. Но прокси может гораздо больше. В том числе и перехватывать вызовы через
new.construct
Ловушка
construct(target, argumentsList) перехватывает вызовы при помощи new.
Она получает исходный объект
target и список аргументов argumentsList.
Пример ниже передаёт операцию создания исходному классу или функции-конструктору, выводя сообщение об этом:
'use strict';functionUser(name,surname){this.name=name;this.surname=surname;}letUserProxy=newProxy(User,{// передаст вызов new User, предварительно сообщив о нёмconstruct:function(target,argumentsList){alert(`Запуск new с аргументами:${argumentsList}`);returnnewtarget(...argumentsList);}});letuser=newUserProxy("Ilya","Kantor");alert(user.name);// Ilya
Полный список
Полный список возможных функций-перехватчиков, которые может задавать
handler:- getPrototypeOf – перехватывает обращение к методу
getPrototypeOf. - setPrototypeOf – перехватывает обращение к методу
setPrototypeOf. - isExtensible – перехватывает обращение к методу
isExtensible. - preventExtensions – перехватывает обращение к методу
preventExtensions. - getOwnPropertyDescriptor – перехватывает обращение к методу
getOwnPropertyDescriptor. - defineProperty – перехватывает обращение к методу
defineProperty. - has – перехватывает проверку существования свойства, которая используется в операторе
inи в некоторых других методах встроенных объектов. - get – перехватывает чтение свойства.
- set – перехватывает запись свойства.
- deleteProperty – перехватывает удаление свойства оператором
delete. - enumerate – срабатывает при вызове
for..inилиfor..of, возвращает итератор для свойств объекта. - ownKeys – перехватывает обращения к методу
getOwnPropertyNames. - apply – перехватывает вызовы
target(). - construct – перехватывает вызовы
new target().
Каждый перехватчик запускается с
handler в качестве this. Это означает, что handler кроме ловушек может содержать и другие полезные свойства и методы.
Каждый перехватчик получает в аргументах
target и дополнительные параметры в зависимости от типа.
Если перехватчик в
handler не указан, то операция совершается, как если бы была вызвана прямо на target.Итого
Proxy позволяет модифицировать поведение объекта как угодно, перехватывать любые обращения к его свойствам и методам, включая вызовы для функций.
Особенно приятна возможность перехватывать обращения к отсутствующим свойствам, разработчики ожидали её уже давно.
Что касается поддержки, то возможности полифиллов здесь ограничены. «Переписать» прокси на старом JavaScript сложновато, учитывая низкоуровневые возможности, которые он даёт.
Поэтому нужна именно браузерная поддержка. Постепенно она реализуется.

| 





0 Մեկնաբանություններ:
Отправить комментарий