С точки зрения вычислений, прокси находятся между вами и тем, с чем вы общаетесь. Этот термин чаще всего применяется к прокси-серверу — устройству между веб-браузером (Chrome, Firefox, Safari, Edge и т. Д.) И веб-сервером (Apache, Nginx, IIS и т. Д.), На котором находится страница. Прокси-сервер может изменять запросы и ответы. Например, это может повысить эффективность за счет кэширования активов с регулярным доступом и предоставления их нескольким пользователям.
Прокси ES6 находятся между вашим кодом и объектом. Прокси-сервер позволяет вам выполнять операции метапрограммирования, такие как перехват вызова для проверки или изменения свойства объекта.
Следующая терминология используется в отношении прокси ES6:
цель
Исходный объект прокси будет виртуализирован. Это может быть объект JavaScript, такой как библиотека jQuery, или нативные объекты, такие как массивы или даже другие прокси.
обработчик
Объект, который реализует поведение прокси, используя …
ловушки
Функции, определенные в обработчике, которые обеспечивают доступ к цели при вызове определенных свойств или методов.
Это лучше всего объяснить на простом примере. Мы создадим целевой объект с именем target
который имеет три свойства:
const target = { a: 1, b: 2, c: 3 };
Теперь мы создадим объект-обработчик, который перехватывает все операции get
. Возвращает свойство цели, когда оно доступно, или 42 в противном случае:
const handler = { get: function(target, name) { return ( name in target ? target[name] : 42 ); } };
Теперь мы создаем новый Proxy, передавая объекты target и handler. Наш код может взаимодействовать с прокси, а не напрямую обращаться к target
объекту:
const proxy = new Proxy(target, handler); console.log(proxy.a); // 1 console.log(proxy.b); // 2 console.log(proxy.c); // 3 console.log(proxy.meaningOfLife); // 42
Давайте расширим обработчик прокси, чтобы он позволял устанавливать только односимвольные свойства от a
до z
:
const handler = { get: function(target, name) { return (name in target ? target[name] : 42); }, set: function(target, prop, value) { if (prop.length == 1 && prop >= 'a' && prop <= 'z') { target[prop] = value; return true; } else { throw new ReferenceError(prop + ' cannot be set'); return false; } } }; const proxy = new Proxy(target, handler); proxy.a = 10; proxy.b = 20; proxy.ABC = 30; // Exception: ReferenceError: ABC cannot be set
Типы ловушек прокси
Мы видели, как get
и set
в действие, которые, вероятно, будут наиболее полезными ловушками. Однако есть несколько других типов ловушек, которые вы можете использовать для дополнения кода обработчика прокси:
- конструкция (target, argList)
Прерывает создание нового объекта с помощьюnew
оператора. - получить (цель, свойство)
TrapsObject.get()
и должен возвращать значение свойства. - установить (цель, свойство, значение)
TrapsObject.set()
и должен установить значение свойства. Вернитеtrue
если успешно. В строгом режиме возвращениеfalse
вызовет исключение TypeError. - deleteProperty (цель, свойство)
Пропускает операциюdelete
в свойстве объекта. Должен возвращать либоtrue
либоfalse
. - применить (target, thisArg, argList)
Ловит вызовы функций объекта. - имеет (цель, свойство)
Ловушкиin
операторах и должны возвращать либоtrue
либоfalse
. - ownKeys (цель)
TrapsObject.getOwnPropertyNames()
и должен возвращать перечисляемый объект. - getPrototypeOf (цель)
TrapsObject.getPrototypeOf()
и должен возвращать объект прототипа или нуль. - setPrototypeOf (цель, прототип)
TrapsObject.setPrototypeOf()
для установки объекта-прототипа. Никакое значение не возвращается. - isExtensible (цель)
TrapsObject.isExtensible()
, который определяет, могут ли у объекта быть добавлены новые свойства. Должен возвращать либоtrue
либоfalse
. - preventExtensions (цель)
TrapsObject.preventExtensions()
, который предотвращает добавление новых свойств к объекту. Должен возвращать либоtrue
либоfalse
. - getOwnPropertyDescriptor (цель, свойство)
TrapsObject.getOwnPropertyDescriptor()
, который возвращает неопределенный объект или объект дескриптора свойства с атрибутами дляvalue
,writable
,get
,set
,configurable
иenumerable
. - defineProperty (цель, свойство, дескриптор)
TrapsObject.defineProperty()
который определяет или изменяет свойство объекта. Должен возвращатьtrue
если целевое свойство было успешно определено, илиfalse
если нет.
Пример прокси 1: профилирование
Прокси позволяют вам создавать универсальные обертки для любого объекта без необходимости изменять код внутри самих целевых объектов.
В этом примере мы создадим прокси-сервер профилирования, который подсчитывает количество обращений к свойству. Во-первых, нам требуется фабричная функция makeProfiler
которая возвращает объект Proxy
и сохраняет состояние счета:
// create a profiling Proxy function makeProfiler(target) { const count = {}, handler = { get: function(target, name) { if (name in target) { count[name] = (count[name] || 0) + 1; return target[name]; } } }; return { proxy: new Proxy(target, handler), count: count } };
Теперь мы можем применить эту прокси-оболочку к любому объекту или другому прокси. Например:
const myObject = { h: 'Hello', w: 'World' }; // create a myObject proxy const pObj = makeProfiler(myObject); // access properties console.log(pObj.proxy.h); // Hello console.log(pObj.proxy.h); // Hello console.log(pObj.proxy.w); // World console.log(pObj.count.h); // 2 console.log(pObj.count.w); // 1
Хотя это тривиальный пример, представьте себе, какие усилия потребуются, если вам приходилось выполнять подсчет доступа к свойствам в нескольких различных объектах без использования прокси.
Пример прокси 2: двустороннее связывание данных
Привязка данных синхронизирует объекты. Обычно он используется в библиотеках JavaScript MVC для обновления внутреннего объекта при изменении DOM и наоборот.
Предположим, у нас есть поле ввода с идентификатором inputname
:
<input type="text" id="inputname" value="" />
У нас также есть объект JavaScript с именем myUser
со свойством id
которое ссылается на этот вход:
// internal state for #inputname field const myUser = { id: 'inputname', name: '' };
Наша первая цель — обновить myUser.name
когда пользователь меняет входное значение. Это может быть достигнуто с помощью onchange
события onchange
на поле:
inputChange(myUser); // bind input to object function inputChange(myObject) { if (!myObject || !myObject.id) return; const input = document.getElementById(myObject.id); input.addEventListener('onchange', function(e) { myObject.name = input.value; }); }
Наша следующая цель — обновить поле ввода, когда мы myUser.name
в коде JavaScript. Это не так просто, но прокси предлагают решение:
// proxy handler const inputHandler = { set: function(target, prop, newValue) { if (prop == 'name' && target.id) { // update object property target[prop] = newValue; // update input field value document.getElementById(target.id).value = newValue; return true; } else return false; } } // create proxy const myUserProxy = new Proxy(myUser, inputHandler); // set a new name myUserProxy.name = 'Craig'; console.log(myUserProxy.name); // Craig console.log(document.getElementById('inputname').value); // Craig
Возможно, это не самый эффективный вариант привязки данных, но прокси-серверы позволяют изменять поведение многих существующих объектов без изменения их кода.
Дальнейшие примеры
В статье Hemanth.HM Negative Array Index в JavaScript предлагается использовать прокси для реализации отрицательных индексов массивов. Например, arr[-1]
возвращает последний элемент, arr[-2]
возвращает предпоследний элемент и т. Д.
Статья Николаса Закаса Создание безопасных по типу свойств с помощью прокси-серверов ECMAScript 6 иллюстрирует, как можно использовать прокси-серверы для обеспечения безопасности типов путем проверки новых значений. В приведенном выше примере мы могли убедиться, что myUserProxy.name
всегда было установлено в строку, и в противном случае myUserProxy.name
ошибку.
Поддержка прокси
Сила прокси не может быть сразу очевидна, но они предлагают мощные возможности метапрограммирования. Брендан Айх, создатель JavaScript, считает, что Прокси — это круто !
В настоящее время поддержка прокси реализована в Node и во всех существующих браузерах, за исключением Internet Explorer 11. Однако обратите внимание, что не все браузеры поддерживают все ловушки. Вы можете получить лучшее представление о том, что поддерживается, просмотрев эту таблицу совместимости браузера на странице прокси MDN.
К сожалению, невозможно заполнить или перенести код прокси ES6 с помощью таких инструментов, как Babel , потому что прокси мощные и не имеют эквивалента ES5.