前言
我们都知道vue3.x版本是通过 Proxy 来实现的,用以解决2.X的一些缺陷。
但是我最近通过看 vue 源代码发现它内部很多地方是 Proxy、Reflect 搭配一起使用的,例:
我们通过上面几张图可以看到 get、set、deleteProperty、has、ownKeys 都用了 Reflect 。
基于此,这就是我写这篇文章的缘由,那我们探索一下为什么要搭配这两使用。
Proxy 和 Reflect 概念
Proxy : 代理 ,可以通过代理对象完成对目标对象的拦截,并在拦截后进行过滤和改写等操作,支持的拦截操作共 13 种Reflect : 反射 ,它提供拦截 JavaScript 操作的方法。这些方法与 Proxy 的方法一一对应,也是 13 种。
Proxy简单使用示例:
const person = {
name: '张三',
get nickName() {
return `${this.name}是坏蛋`
}
}
const personProxy = new Proxy(person, {
get(target, key, receiver) {
console.log('进来了吧')
return target[key]
}
})
console.log(personProxy.nickName)
// 先打印了:进来了吧
// 然后打印了:张三是坏蛋
通过上面这个示例,我们发现他成功代理了,虽然我们没做什么处理。
不过 get 里面的 receiver 这个参数我们还没用到,receiver 在这个时候表示代理对象(也就是personProxy)。
我们在 get 里面的return target[key] ,不能return receiver[key] ,否则会死循环。
我们再来个 Proxy 示例2:
const person = {
name: '张三',
get nickName() {
console.log(this)
return `${this.name}是坏蛋`
}
}
const personProxy = new Proxy(person, {
get(target, key, receiver) {
return target[key]
}
})
const person2 = {
name: '李四'
}
Object.setPrototypeOf(person2, personProxy)
console.log(person2.nickName)
上面这个示例种的console.log(this) 和 console.log(person2.nickName) 会打印上面呢?
我们希望的应该是 person2这个对象 和 李四是坏蛋 是吧,但实际上没有如期打印。
首先person2上面没有nickName这个属性,所以他会去personProxy上面找,然后返回了 person 上面的 nickName属性,nickName中的this指向了person他自己。
所以console.log(this) 打印出来的是 person 对象,console.log(person2.nickName) 打印出来的是person中的name,也就是张三是坏蛋 。
那我们怎么才能让他按照我们设想的那样打印李四是坏蛋 ?
我们来到第三个示例:
const person = {
name: '张三',
get nickName() {
console.log(this)
return `${this.name}是坏蛋`
}
}
const personProxy = new Proxy(person, {
get(target, key, receiver) {
return Reflect.get(target, key, receiver)
}
})
const person2 = {
name: '李四'
}
Object.setPrototypeOf(person2, personProxy)
console.log(person.nickName) // 张三是坏蛋
console.log(personProxy.nickName) // 张三是坏蛋
console.log(person2.nickName) // 李四是坏蛋
/*
* 三次this打印的顺序如下:
* { name: '张三', nickName: [Getter] }
* { name: '张三', nickName: [Getter] }
* { name: '李四' }
* */
我们通过在 get 里面 return Reflect.get(target, key, receiver) 来实现按我们想要的那样打印出来。
我们给 Reflect 的 get 传递第三个参数(Proxy中的receiver ),然后他就会修改调用时的this 指向(也就是把指向person 修改为:指向 person2 )。
我们引用阮老师中的 Reflect 代码片段来说明一下 receiver ,例:
var myObject = {
foo: 1,
bar: 2,
get baz() {
return this.foo + this.bar;
},
};
var myReceiverObject = {
foo: 4,
bar: 4,
};
Reflect.get(myObject, 'baz', myReceiverObject) // 8
如果name 属性部署了读取函数(getter),则读取函数的this 绑定receiver 。
|