一,代理基础
1,创建空代理
代理使用Proxy函数创建,需要传入两个参数,目标对象和处理程序对象,其中处理程序对象可以传入一个简单的自变量作为处理程序,这么做会使所有程序畅通无阻的进行访问目标对象,实例如下,
const target = {
id:123
}
const proxy = new Proxy(target,{})
console.log(target.id) //123
console.log(proxy.id) //123
2,定义捕捉器
使用代理的主要目的是为了创建捕捉器,捕捉器可以理解成基本操作中的拦截器。每个处理程序对象可以包含多个捕捉器也可以不包含捕捉器,每个捕捉器都对应一种基本操作,可以直接或间接在对象上进行调用。每次在代理商调用这些操作时,代理可以在这些操作达到目标对象之前进行调用捕获器函数,从而实现拦截并修改响应的行为。
例:定义一个get()捕捉器,在进行ES操作时触发get(),
const target = {
id:132
}
const handler = {
get(){
return "this is bar"
}
}
const proxy = new Proxy(target, handler)
console.log(proxy.id) //this is bar
3,捕捉器参数与反射API
所有的捕捉器都可以访问到相应的参数,基于这些参数可以重建被捕捉方法的原始行为,比如get()捕捉器会接收到目标对象,要查询的属性和代理对象三个参数。
const target = {
id:123
}
const handler = {
get(tarpTarget, property, receiver){
console.log(tarpTarget === target) //true
console.log(property) //123
console.log(receiver === proxy) //true
}
}
const proxy = new Proxy(target, handler)
proxy.id
有了如上参数就可以重建被捕获的方法的原始行为:
const target = {
id: 123
}
const handler = {
get(){
get(tarpTarget, property, receiver){
return tarpTarget[property]
}
}
}
const proxy = new Proxy(target, handler)
console.log(proxy.id) //123
处理程序对象中所有可以捕捉的方法都有对应的反射(Reflect)API 方法。这些方法与捕获器拦截的方法具有相同的名称和函数签名,而且也具有与被拦截方法相同的行为,因此也可以如下定义出空代理对象:
const target = {
id: 123
}
const handler = {
get(){
get: Reflect.get
}
}
const proxy = new Proxy(target, handler)
console.log(proxy.id) //123
或直接如下定义:
const target = {
id: 123
}
const proxy = new Proxy(target, Reflect)console.log(proxy.id) //123
二,代理捕捉器与反射方法
1,get()
get()捕捉器会在获取值得时候被调用,对应的反射API方法为Reflect.get()。
const myTarget = {
foo: 'get'
}
const handler = {
get(target ,property,receiver){ return Reflect.get(...arguments)
}
}
const proxy = new Proxy(myTarget, handler)
console.log(proxy.get) //get
(1),返回值
返回值无限制。
(2),拦截的操作
proxy.property()
proxy[property]
Oject.create(proxy)[property]
Reflect.get(proxy, property, receiver)
(3),捕捉器处理程序参数
target:目标对象。
property:引用的目标对象上的字符串键属性。
receiver:代理对象或继承对象的对象。
(4),捕捉器不变
如果target.property 不可写且不可配置,则处理程序返回的值必须与target.property匹配。
如果target.property 不可配置且[[Get]]特性为undefined,处理程序的返回值必须是undefined。
2,set()
set()捕捉器会在设置属性值的操作中被调用。对应的反射API方法为Reflect.set()
cosnt target = {};
const proxy = new Proxy(target, {
set(target, property, value, receiver){
console.log('执行set()')
return Reflect.set(...arguments)
}
})
proxy.foo = 'Hello, set' //执行set
(1),返回值
返回true表示成功;返回false表示失败,严格模式下会返回TypeError。
(2),拦截的操作
proxy.property = value
proxy[property] = value
Object.create(proxy)[property] = value
Reflect.set(target, property, value, receiver)
(3),捕捉器处理程序参数
target:目标对象。
property:引用的目标对象上的字符串键属性。
value:要赋值给属性的参数。
receiver:接收最初赋值的对象。
(4),捕捉器不变式
如果target.property不可写且不可配置,则不能修改目标属性的值。
如果target.property 不可配置且[[Set]]特性为undefined,则不能修改目标的值。
在严格模式下,处理程序返回false时,会抛出TypeError。
3,has()
has()捕捉器会在 in 操作符中被调用。对应的反射API时Reflect.has()
const target = {}
const handler = {
has(target, property){
console.log('set')
return Reflect.has(...arguments)
}
}
const proxy = new Proxy(target, handler)
'name' in proxy //set()
(1),返回值
has()必须返回布尔值,表示属性是否存在。返回非布尔值会被转换成布尔值。
(2),拦截的操作
property in proxy
property in Object.create(proxy)
with(proxy) {(property); }
Reflect.has(target, property)
(3),捕获器处理程序参数
target:目标对象。
property:引用的目标对象上的字符创键属性。
(4),捕获器不变式
如果target.property存在且不可配置,则处理程序必须返回true
如果target.property存在且目标对象不可扩展,则处理程序必须返回true
4,defineProperty()
defineProperty()捕捉器会在Object.defineProperty()中被调用,对应的反射API方法应为Reflect.defineProperty()
const target = {}
const handler = {
defineProperty(target, property){
console.log('defineProperty')
return Reflect.defineProperty(...arguments)
}
}
const proxy = new Proxy(target, handler)
Object.defineProperty(proxy,"name",{value:"张三"}) //defineProperty
(1),返回值
defineProperty()必须返回布尔值,表示属性是否成功定义。返回非布尔值会被默认转换为布尔值。
(2),拦截的操作
Object.defineProperty(proxy, property, descriptor)
Reflect.defineProperty(proxy, property, descriptor)
(3),捕捉器处理程序参数
target:目标对象。
property:引用的目标对象上的字符串键属性。
descriptor:包含可选的enumerable、configurable、writable、value、get和set定义的对象。
(4),捕捉器不变式
如果目标对象不可扩展,则无法定义属性。
如果目标对象有一个可配置的属性,则不能添加同名的不可配置属性。
如果目标对象有一个不可配置的属性,则不能添加同名的可配置属性。
三,代理模式
1,跟踪属性访问
通过捕获get、set、has等操作,可以知道对象属性什么时候被访问、被查询。把视线相应捕获器的某个对象代理放到应用中,可以监控这个对象何时被访问过:
const user = {
name: "张三"
}
const handler = {
get(target, property, receiver){
console.log(`Getting ${property}`)
return Reflect(...arguments)
},
set(target, property, value, receiver){
console.log(`Setting ${prperty} = ${value}`)
return Reflect.set(...arguments)
}
}
const proxy = new Proxy(user, handler)
proxy.name //Getting name
proxy.age = "18" //Setting age = 18
2,隐藏属性
代理的内部可以实现对外部代码是不可见得,因此隐藏目标对象上的属性轻而易举。比如:
const hiddenProperties = ['foo', 'bar']
const targetObject = {
foo: 1,
bar: 2,
baz: 3,
}
const proxy = new Proxy(targetObject, {
get(target, property){
if(hiddenProperties.includes(property)){
return undefined
}else{
return Reflect.get(...arguments)
}
},
has(target, property){
if(hiddenProperties.includes(property)){
return false
}else{
return Reflect(...arguments)
}
}
})
console.log(proxy.foo) //undefined
console.log(proxy.bar) //undefined
console.log(proxy.baz) //3
console.log('foo' in proxy) //false
console.log('bar' in proxy) //false
console.log('baz' in proxy) //true
3,属性验证
由于所有赋值都会触发 set() 捕获器,所以可以根据所赋的值决定是否允许操作
const targetNumbers = {
onlyNumber = 0
}
const proxy = new Proxy(targetNumbers, {
set(target, property, value){
if(typeof value != 'number'){
return false
}else{
return Reflect.set(...arguments)
}
}
})
proxy.onlyNumber = 1
console.log(proxy.onlyNumber) //1
proxy.onlyNumber = '2'
console.log(proxy.onlyNumber) //1 未修改成功