const address = {
country: "Russia",
city: "Moscow"
}
const addressProxy = new Proxy(address, {
// здесь мы определяем какое именно действие
// по объекту address мы хотим перехватить
// например, мы можем перехватить тот момент
// когда что-то пытается получить доступ
// до одного из значений объекта по его ключу
// для этого мы поставим ловушку/handler
get: (target, prop) => {
return target[prop]
}
})
console.log(addressProxy.country)
// Russia
console.log(addressProxy.city)
// Moscow
console.log(addressProxy.street)
// undefined
console.log("country" in addressProxy)
// true
console.log("city" in addressProxy)
// true
console.log("metadata" in addressProxy)
// false
函数也可以被代理,例如,我们可以跟踪它被调用的时刻:
const print = text => console.log(text)
const printProxy = new Proxy(print, {
apply: (target, thisArg, argArray) => {
return target.apply(thisArg, argArray)
}
})
printProxy("это тест, мы успешно перехватили вызов функции")
// это тест, мы успешно перехватили вызов функции
这将使我们无需任何额外的努力就可以实现以下逻辑:
// давайте отфильтруем плохие слова
// запретив нашей функции их вывод
// и оставим только приятный слуху язык
const print = text => console.log(text)
const printProxy = new Proxy(print, {
apply: (target, thisArg, argArray) => {
// для простоты примера представим что
// в массиве запрещенных слов сейчас
// только одно слово
const badWords = ["ругательство"]
if (badWords.includes(argArray[0])) {
return target("***")
}
return target(argArray[0])
}
})
printProxy("спасибо")
// спасибо
printProxy("ругательство")
// ***
const users = [
{ id: 1, name: "Иван" },
{ id: 2, name: "Мария" },
{ id: 3, name: "Антон" }
]
// что бы найти пользователя с ID равным 3
// нам нужно пройтись по всему массиву
// и сверить ID у каждого пользователя
// пока мы не найдем нужный
const targetUser = users.find(user => user.id === 3)
console.log(targetUser)
// { id: 3, name: "Антон" }
如果只有三个用户,原则上没有问题,但如果有 100,000 个用户,我们该怎么办?
(这种体积的持续隔板将非常昂贵)
const users = [
{ id: 1, name: "Иван" },
{ id: 2, name: "Мария" },
{ id: 3, name: "Антон" }
// ...
// и еще более чем
// 100.000 записей
]
const chinaAddress = new Address(argsArr)
// sidenote
// такой подход приведет к созданию
// и использованию итератора
可以这样写:
const chinaAddress = Reflect.construct(Address, argsArr)
// sidenote
// такой подход не потребует задействования итератора
// поскольку construct() использует
// length и прямой доступ
// что в целом положительно повлияет на оптимизацию
代理对象是“陷阱”,我们可以通过它从对象、类、函数等中拦截我们需要的“事件”。
如果您完全不熟悉它们,那么我会说这与Browser API中的eventListners非常相似,因为在Proxy的帮助下,我们可以绑定并在必要时从上面列出的那些实体中拦截我们需要的事件.
这样的陷阱是什么样子的:
现在我们得到了对象的值,但是我们是通过代理来完成的,所以我们有机会在放弃之前做任何有价值的事情:
也就是说,现在我们可以在“事件” get()上放置一个 eventListener,在它工作之后,我们拦截了对特定键的请求并将其值更改为我们需要的格式。
现在我们可以安全地向我们的对象添加新键:
但很容易通过绑定到“事件” set()来禁用它:
我们还可以隐藏某些字段:
如果现在我们“询问”是否存在这样的字段,我们会得到:
函数也可以被代理,例如,我们可以跟踪它被调用的时刻:
这将使我们无需任何额外的努力就可以实现以下逻辑:
我想现在代理的主要思想已经变得更加清晰了。另外,我想指出,对于每个实体,都有特定于它们的处理程序,例如,对于函数,它是apply(),对于类(以及以new operator 开头的所有内容)construct()。
可以在此处找到处理程序的完整列表。
在大型现代项目中,经常使用代理,常见的应用领域之一是各种“优化”。
假设我们有一组用户,我们需要通过 ID 找到我们需要的用户:
如果只有三个用户,原则上没有问题,但如果有 100,000 个用户,我们该怎么办?
(这种体积的持续隔板将非常昂贵)
我们可以代理Array类,并为其添加一个处理程序construct(),这将允许我们“附加”到每个新实例的初始化时刻。
在其中,我们迭代我们的数组并为每个条目分配一个等于用户 ID 的索引:
在创建新的 indexedArray 实例时,迭代将只执行一次:
之后,我们就可以尽可能简单地获取我们需要的用户:
我特意简化了上面的例子,以便于理解主要思想,但是,为了保持完整的功能,包括添加/删除/更改字段等,它需要最终确定。
代理和反射是标准的内置对象,但如果前者旨在“拦截”和“重写”被代理对象的基本操作,那么后者提供了处理“拦截”操作的方法。
Reflect的所有方法和属性都是静态的,而对象本身是非函数的,也就是说它是不可构造的,我们不能和new操作符一起使用,也不能作为函数调用。
Reflect 对象的函数名称与 Proxy 中的处理程序名称相同,其中一些重复了Object类的方法的功能,尽管有一些区别。
这就是标准化。
例如,apply() 方法存在于所有构造函数(许多实现)中,但在 Reflect 中删除(一种实现)。
ESLint的默认设置已经表明,而不是:
值得使用:
因此,例如,而不是写:
可以这样写:
也就是说,通过选择 Reflect 而不是标准方法,我们只是在朝着现代 JavaScipt 前进的方向前进。
Proxy是一个构造函数,允许您对对象进行包装,您可以在其中覆盖标准行为,例如,访问对象属性正如您在示例中看到的,访问目标上不存在的属性将返回预定义的值,而不是
undefined.Reflect是一个提供处理对象的方法的对象,其中一些方法是重复的Object,例如Object.defineProperty-Reflect.defineProperty以及复制某些运算符功能的方法,
new-Reflect.constructdelete-Reflect.deleteProperty和其他人。因此,在这个对象中,我们决定收集处理对象内部的方法。
Proxy积极使用Vuejs