Vue3 Proxy 与 Reflect 结合使用的目的

前言

先说结论,Proxy 与 Reflect 结合使用是为了在触发 proxy 的时候纠正 this 的指向问题

了解过 Proxy 的同学都知道,Proxy 会拦截一个对象的 get 和 set 方法,并且返回一个新的对象,我们去修改这个新对象就会触发 get 和 set 方法。

那么由于存在两个不同的对象,那么是不是就会存在两个不同的this,这在某些时候会有一些意料之外的情况发生。

Proxy

首先先来简单使用一下 Proxy

js 复制代码
const text = {
  name: 'xiaomi',
}
  
const proxy = new Proxy(text, {
  get(target, key) {
    console.log('🚀 触发拦截')
    return target[key]
  },
})
  
proxy.name

// 🚀 触发拦截

在访问 proxy 的属性的时候触发了拦截,target表示原对象,key表示访问的key

但是 get 方法还存在 第三个参数 receiver

接下来我们举两个例子来看一下意外情况

意外情况

1

先了解一下 js 原生对象中的 get set 这个结合 proxy 会造成一些理想外的情况

js 复制代码
const text = {
  name: 'xiaomi',
  get value() {
    return this.name
  },
}

const text2 = {
  name: 'xiaohong',
}

const proxy = new Proxy(text, {
  get(target, key, receiver) {
    return target[key]
  },
})

Object.setPrototypeOf(text2, proxy)

console.log(text2.value) // xiaomi

text2 继承了 proxy,并且因为 text2 身上没有 value 属性,所以一旦访问 value 就会触发陷阱,但是你要是访问 name 因为 text2 身上有这个属性,所以不会向父元素查找,就不会触发陷阱。同理,如果 obj 身上有 value 属性,那就不会触发陷阱。

所以你可以看到,虽然我们触发了 proxy 的 get 陷阱,但是 target 对象是 text,所以 this.name 就会得到 text 的name 也就是 xiaomi。这显然不是我们想要的,访问了 text2 那么this.name 就希望是 text2 的name,也就是 xiaohong

2

另一种情况,当我们访问的这个 get 属性有去 访问当前对象的其他属性的时候,按道理来说应该继续触发 proxy 的拦截

js 复制代码
const p1 = {
  name: '张',
  get fullName() {
    return this.name
  },
}

const proxy = new Proxy(p1, {
  get(target, key, receiver) {
    console.log('触发了 getter')
    return target[key]
  },
})

console.log(proxy.fullName)

但是上面这个例子,就只会触发一次拦截,这也是因为,在 return target[key] 触发 fullName 的时候获取到的 this 为 p1。

从上面两个例子可以发现,在 proxy 的 get 方法当中,在一些特殊情况我们获取到的this并不是我们想要的,所以这时候就轮到 receiver 登场

receiver

get(target, key, receiver) get 中的第三个参数是 receiver 这个属性代表当前触发这个陷阱的上下文,比如第一个例子中的 receiver 就是继承了text 的 text2 对象

js 复制代码
const text = {
  name: 'xiaomi',
  get value() {
    return this.name
  },
}

const text2 = {
  name: 'xiaohong',
}

const proxy = new Proxy(text, {
  get(target, key, receiver) {
    console.log('🚀', receiver === text) // false
    console.log('🚀', receiver === text2) // true
    return target[key]
  },
})

Object.setPrototypeOf(text2, proxy)
console.log(text2.value) // xiaomi

那么我们在这时候就又可以引出 Reflect 对象,这个对象的get方法作用是跟 target[key] 相同的,唯一的不同点就是 ,它也可以传入第三个参数 receiver

js 复制代码
target[key]
  
Reflect.get(target, key, receiver)

Reflect.get 的第三个参数 receiver 可以修改 this 指向

那么在这时候我们就能够得到正确的返回

js 复制代码
const text = {
  name: 'xiaomi',
  get value() {
    return this.name
  },
}

const text2 = {
  name: 'xiaohong',
}

const proxy = new Proxy(text, {
  get(target, key, receiver) {
    console.log('🚀', receiver === text) // false
    console.log('🚀', receiver === text2) // true
    return Reflect.get(target, key, receiver)
  },
})

Object.setPrototypeOf(text2, proxy)
console.log(text2.value) // xiaohong

对于第二个例子也是一样的,会触发两次 get 陷阱,感兴趣的伙伴可以自己尝试一下

总结

在阅读 vue3 源码的过程一定会碰到 Proxy 与 Reflect 相关的问题,这里结合大佬们的文章记录了一下自己的理解,如果有写的不好的地方欢迎评价。

引用

为什么Proxy一定要配合Reflect使用?

相关推荐
GreenTea25 分钟前
从 Claw-Code 看 AI 驱动的大型项目开发:2 人 + 10 个自治 Agent 如何产出 48K 行 Rust 代码
前端·人工智能·后端
渣渣xiong1 小时前
从零开始:前端转型AI agent直到就业第五天-第十一天
前端·人工智能
布局呆星1 小时前
Vue3 | 组件通信学习小结
前端·vue.js
C澒1 小时前
IntelliPro 企业级产研协作平台:前端智能生产模块设计与落地
前端·ai编程
OpenTiny社区3 小时前
重磅预告|OpenTiny 亮相 QCon 北京,共话生成式 UI 最新技术思考
前端·开源·ai编程
前端老实人灬3 小时前
web前端面试题
前端
Moment3 小时前
AI 全栈指南:NestJs 中的 Service Provider 和 Module
前端·后端·面试
IT_陈寒3 小时前
为什么我的JavaScript异步回调总是乱序执行?
前端·人工智能·后端
Moment3 小时前
AI全栈入门指南:NestJs 中的 DTO 和数据校验
前端·后端·面试