что такое прокси
Во-первых, давайте проясним,Proxy
Что это значит, слово в переводе, этоиграет роль.
Можно понять, что есть очень популярная звезда, которая открыла учетную запись Weibo.Эта учетная запись очень активна, отвечает фанатам, везде ставит лайки и т. д., но я не могу ее поддерживать на самом деле.
Но за операцией стоит еще один человек или команда, мы можем назвать их агентами, потому что публикуемый ими Weibo представляет смысл самих звезд.
P.S. Насильно приведу пример, ибо за звездами не гонюсь, просто предполагаю, что может быть такая оперативка
Это заменяется наJavaScript
среди них можно понять, что对象
или函数
работа через прокси.
Прокси в JavaScript
Прокси — это новый API, представленный в ES6, который можно использовать для определения настраиваемого поведения для различных основных операций с объектами.
(упоминается в документации какtraps
, думаю, это можно понимать как зацепку за разное поведение объектов)
С ним можно делать много интересных вещей, и он будет очень эффективен, когда нам нужно контролировать поведение некоторых объектов.
Прокси синтаксис
СоздаватьProxy
Экземпляр должен передавать два параметра
-
target
Проксируемый объект, который может бытьobject
илиfunction
-
handlers
Обработка различных режимов работы прокси-объекта
let target = {}
let handlers = {} // do nothing
let proxy = new Proxy(target, handlers)
proxy.a = 123
console.log(target.a) // 123
В случае, когда второй параметр является пустым объектом, его можно понимать как неглубокую копию первого параметра.
(Прокси должен быть мелкой копией, если это глубокая копия, он потеряет значение прокси)
Ловушки (агенты для различного поведения)
Как и в приведенном выше примере кода, если соответствующийtrap
, это не будет иметь никакого эффекта, это эквивалентно прямой операцииtarget
.
когда мы пишемtrap
Позже, когда будет выполнено соответствующее действие, сработает наша callback-функция, и мы будем управлять поведением проксируемого объекта.
Два наиболее часто используемыхtrap
должно бытьget
а такжеset
.
ранние годаJavaScript
Имеет свойство, которое устанавливается при определении объектаgetter
,setter
:
let obj = {
_age: 18,
get age () {
return `I'm ${this._age} years old`
},
set age (val) {
this._age = Number(val)
}
}
console.log(obj.age) // I'm 18 years old
obj.age = 19
console.log(obj.age) // I'm 19 years old
Как описано в этом коде, мы устанавливаем свойство_age
, а затем установитеget age
а такжеset age
.
Тогда мы можем напрямую вызватьobj.age
Чтобы получить возвращаемое значение, вы также можете присвоить ему значение.
Есть несколько недостатков в этом:
- Для каждого атрибута, подлежащего проксированию, соответствующий
getter
,setter
. - Также должен быть магазин, в котором хранится реальная стоимость
key
(если мы прямоgetter
внутренний вызовthis.age
Переполнение стека происходит потому, что всякий раз, когда вы вызываетеthis.age
Значение будет активированоgetter
).
Proxy
Он отлично решает эти две проблемы:
let target = { age: 18, name: 'Niko Bellic' }
let handlers = {
get (target, property) {
return `${property}: ${target[property]}`
},
set (target, property, value) {
target[property] = value
}
}
let proxy = new Proxy(target, handlers)
proxy.age = 19
console.log(target.age, proxy.age) // 19, age : 19
console.log(target.name, proxy.name) // Niko Bellic, name: Niko Bellic
Мы создаемget
,set
дваtrap
Чтобы управлять всеми операциями единым образом, вы можете увидеть, что при измененииproxy
Тем временем,target
содержание также было изменено, и нашproxy
Поведение имеет некоторую особую обработку.
И нам не нужно использовать дополнительныйkey
чтобы сохранить реальную стоимость, потому что мы находимся вtrap
Внутренняя операцияtarget
объект вместоproxy
объект.
Что делать с прокси
потому что с помощьюProxy
Наконец, поведение объекта в основном управляемо, поэтому мы можем использовать его для некоторых вещей, которые раньше реализовать было сложнее.
Ниже перечислены несколько простых применимых сценариев.
Решите проблему, что свойство объекта не определено
Как бороться с приобретением некоторых свойств объекта глубокого уровняundefined
был болезненным процессом, если мы используемProxy
Это может быть хорошо совместимо с этой ситуацией.
(() => {
let target = {}
let handlers = {
get: (target, property) => {
target[property] = (property in target) ? target[property] : {}
if (typeof target[property] === 'object') {
return new Proxy(target[property], handlers)
}
return target[property]
}
}
let proxy = new Proxy(target, handlers)
console.log('z' in proxy.x.y) // false (其实这一步已经针对`target`创建了一个x.y的属性)
proxy.x.y.z = 'hello'
console.log('z' in proxy.x.y) // true
console.log(target.x.y.z) // hello
})()
были представленыget
, и выполнять логическую обработку внутри, если мы хотимget
Ценность исходит из несуществующегоkey
, то у нас будетtarget
Создайте соответствующий в этомkey
, а затем возвращает цель для этогоkey
прокси-объект.
Это гарантирует, что наша операция со значением не выдастcan not get xxx from undefined
Но у этого есть небольшой недостаток, который заключается в том, что если вам приходится судить об этомkey
Есть ли способ пройтиin
оператор судить, а не напрямую черезget
судить.
Совместимость Обработка обычных функций и конструкторов
если мы предоставимClass
возразить кому-то другому или сказатьES5
вариант конструктора.
если не используетсяnew
ключевое слово для вызова,Class
Объект выдает исключение напрямую, в то время какES5
конструктор вthis
Указание на него становится областью видимости при вызове функции.
мы можем использоватьapply
этоtrap
быть совместимым с этой ситуацией:
class Test {
constructor (a, b) {
console.log('constructor', a, b)
}
}
// Test(1, 2) // throw an error
let proxyClass = new Proxy(Test, {
apply (target, thisArg, argumentsList) {
// 如果想要禁止使用非new的方式来调用函数,直接抛出异常即可
// throw new Error(`Function ${target.name} cannot be invoked without 'new'`)
return new (target.bind(thisArg, ...argumentsList))()
}
})
proxyClass(1, 2) // constructor 1 2
мы использовалиapply
Чтобы проксировать какое-то поведение, оно будет срабатывать при вызове функции, потому что мы точно знаем, что прокси — этоClass
или конструктор, поэтому мы непосредственно вapply
используется вnew
ключевое слово для вызова делегированной функции.
и если мы хотим ограничить функцию, запретить использованиеnew
ключевое слово для вызова, вы можете использовать другоеtrap
:construct
function add (a, b) {
return a + b
}
let proxy = new Proxy(add, {
construct (target, argumentsList, newTarget) {
throw new Error(`Function ${target.name} cannot be invoked with 'new'`)
}
})
proxy(1, 2) // 3
new proxy(1, 2) // throw an error
Обернуть выборку с помощью прокси
Отправка запросов на интерфейсе, то, что мы часто используем сейчас, должно бытьfetch
, собственный API.
мы можем использоватьProxy
обернуть его, чтобы сделать его более удобным в использовании.
let handlers = {
get (target, property) {
if (!target.init) {
// 初始化对象
['GET', 'POST'].forEach(method => {
target[method] = (url, params = {}) => {
return fetch(url, {
headers: {
'content-type': 'application/json'
},
mode: 'cors',
credentials: 'same-origin',
method,
...params
}).then(response => response.json())
}
})
}
return target[property]
}
}
let API = new Proxy({}, handlers)
await API.GET('XXX')
await API.POST('XXX', {
body: JSON.stringify({name: 1})
})
правильноGET
,POST
Осуществляется слой инкапсуляции, который может быть пропущен непосредственно через.GET
Это способ вызова и установки некоторых общих параметров.
Реализовать простой инструмент утверждения
Утверждение должен знать каждый, кто писал тест.
console.assert
это инструмент утверждения, который принимает два параметра, если первыйfalse
, второй параметр будетError message
бросать.
мы можем использоватьProxy
Чтобы сделать инструмент, который напрямую назначает утверждение.
let assert = new Proxy({}, {
set (target, message, value) {
if (!value) console.error(message)
}
})
assert['Isn\'t true'] = false // Error: Isn't true
assert['Less than 18'] = 18 >= 19 // Error: Less than 18
Подсчитайте количество вызовов функций
При создании сервера мы можем использоватьProxy
Делегируйте некоторые функции для подсчета количества звонков за определенный период времени.
Возможно, вы сможете использовать его при анализе производительности позже:
function orginFunction () {}
let proxyFunction = new Proxy(orginFunction, {
apply (target, thisArg. argumentsList) {
log(XXX)
return target.apply(thisArg, argumentsList)
}
})
все ловушки
перечислено здесьhandlers
все определяемое поведение(traps):
Конкретные можно посмотретьMDN-Proxy
Есть также несколько примеров
traps | description |
---|---|
get | получитьkey стоимость |
set | установитьkey стоимость |
has | использоватьin оператор для определенияkey он существует |
apply | вызов функции, только если прокси-объектfunction действительный, когда |
ownKeys | Получить весь целевой объектkey
|
construct | Функция вызывается инстанцированием, только если прокси-объектfunction действительный, когда |
isExtensible | Определить, является ли объект расширяемым,Object.isExtensible агент |
deleteProperty | удалить одинproperty
|
defineProperty | определить новыйproperty
|
getPrototypeOf | получить прототип объекта |
setPrototypeOf | установить объект-прототип |
preventExtensions | установить объект как нерасширяемый |
getOwnPropertyDescriptor | Получить собственное имущество(не пойдет в цепочку прототипов, чтобы найти)описание свойства |