ES6 Proxy 与 Reflect
概述
- Proxy 与 Reflect 是 ES6 为了操作对象引入的 API。
- Proxy 可以对目标对象的读取,函数调用等操作进行拦截,然后进行处理。它不直接操作对象,而是像代理模式,通过【对象】的【代理对象】进行操作,在进行这些操作时,可以添加一些额外的操作。
- Reflect 可以用于获取【目标对象】的行为,它与 Object 类似,但更容易读。为了操作对象提供了一种更优雅的方式。它的方法与 Proxy 是对应的。
Proxy 基本用法
const target = {
name: 'Tom',
age: 8
}
const handler = {
get: function(target, key) {
console.log('get', key, target[key]); // get name Tom
return target[key];
},
set: function(target, key, value) {
console.log('set', key, value); // set age 9
target[key] = value;
}
}
let proxy = new Proxy(target, handler)
proxy.name // 实际执行 handler.get
proxy.age = 9 // 实际执行 handler.set
console.log(proxy) // Proxy {name: "Tom", age: 9, male: "man"}
console.log(target) // {name: "Tom", age: 9, male: "man"}
// 通过 new Proxy() 新建实例时,其实是对【目标对象】进行了浅拷贝,因此【目标对象】与【代理对象】会相互影响
Proxy 与 Reflect 基本用法
const handler = {
get(target, key, receiver) {
const ownKeys = Reflect.ownKeys(target)
// 只处理本身(非原型) 属性---原型的属性例如:push
if (ownKeys.includes(key)) {
console.log('get', key)
}
const result = Reflect.get(target, key, receiver)
return result // 返回结果
},
set(target, key, val, receiver) {
// 重复的数据或值没有改变不处理
// 场景:
// 1. 数组push()的时候,length不用重设;
// 2. proxyTom.age 已经是 8 再执行 proxyTom.age = 8
const oldVal = target[key]
if (val === oldVal) {
return true
}
const result = Reflect.set(target, key, val, receiver)
console.log('set', key, val)
console.log('result---', result)
return result // 是否设置成功 true:成功,false:失败
},
deleteProperty(target, key, receiver) {
const result = Reflect.deleteProperty(target, key)
console.log('deleteProperty', key)
console.log('result---', result)
return result // 是否删除成功 true:成功,false:失败
}
}
const Tom = {
name: 'Tom',
age: 8,
male: 'boy'
}
const proxyTom = new Proxy(Tom, handler)
proxyTom.age
proxyTom.age = 9
delete proxyTom.male
console.log('%c------------------------', 'color:blue')
const list = [1, 2, 3]
const proxyList = new Proxy(list, handler)
proxyList.push(4)
console.log('%c------------------------', 'color:blue')
Proxy 中的 receiver
情况一:
const Tom = {
name: 'Tom'
}
const proxy = new Proxy(Tom, {
get(target, key, receiver) {
console.log(receiver === proxy) // true
return target[key]
}
});
proxy.name
情况二:
const Tom = {
name: 'Tom'
}
const proxy = new Proxy(Tom, {
get(target, key, receiver) {
console.log(receiver === proxy) // false
console.log(receiver === Jack) // true
return target[key]
},
});
const Jack = {}
// 将Jack的原型指向Tom的代理对象proxy
Object.setPrototypeOf(Jack, proxy);
Jack.name
总结:
- 谁读写属性,就是谁接收(即:谁读写属性,谁就是
receiver
),例如:上面情况一proxy.name
,proxy
读取name
属性,所以proxy
就是receiver
; 情况二Jack.name
同理,Jack
是receiver
什么情况 Proxy 要配合 Reflect 使用?
const Cat = {
name: 'miaomiao',
get value() {
return this.name
}
}
const handler = {
get(target, key, receiver) {
return Reflect.get(target, key) // 相当于 return target[key]
}
}
const proxy = new Proxy(Cat, handler)
const Tom = {
name: 'tomtom'
}
Object.setPrototypeOf(Tom, proxy)
console.log(Tom.value) // miaomiao
粗略步骤:
Tom.value
时,由于Tom
本身不存在value
属性,此时根据原型链向上读取它原型对象proxy
的value
- 触发
proxy
上的get value()
- 触发
handler
的get
陷阱 - 进入
get
陷阱时,target
为源对象Cat
,key
为value
get
陷阱中返回Reflect.get(target, key)
相当于target[key]
Tom.value
原本的调用方Tom
,在get
陷阱中被修改成了对应的target
也就是Cat
- 导致最后打印出了
Cat[value]
也就是 miaomiao
我们希望的是,Tom.value
返回的是 tomtom,所以我们要把代码改成:
const Cat = {
name: 'miaomiao',
get value() {
return this.name
}
}
const handler = {
get(target, key, receiver) {
-
return Reflect.get(target, key) // 相当于 return target[key]
+
return Reflect.get(target, key, receiver) // 可以理解为 target[key].call(receiver)
}
}
const proxy = new Proxy(Cat, handler)
const Tom = {
name: 'tomtom'
}
Object.setPrototypeOf(Tom, proxy)
console.log(Tom.value) // tomtom