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使用?

相关推荐
a东方青2 分钟前
vue3学习笔记之属性绑定
vue.js·笔记·学习
东部欧安时9 分钟前
研一自救指南 - 07. CSS面向面试学习
前端·css
沉默是金~20 分钟前
Vue+Notification 自定义消息通知组件 支持数据分页 实时更新
javascript·vue.js·elementui
涵信21 分钟前
第十二节:原理深挖-React Fiber架构核心思想
前端·react.js·架构
ohMyGod_12326 分钟前
React-useRef
前端·javascript·react.js
每一天,每一步28 分钟前
AI语音助手 React 组件使用js-audio-recorder实现,将获取到的语音转成base64发送给后端,后端接口返回文本内容
前端·javascript·react.js
上趣工作室28 分钟前
vue3专题1------父组件中更改子组件的属性
前端·javascript·vue.js
冯诺一没有曼32 分钟前
无线网络入侵检测系统实战 | 基于React+Python的可视化安全平台开发详解
前端·安全·react.js
getapi36 分钟前
flutter app实现分辨率自适应的图片资源加载
前端·javascript·flutter
—Qeyser1 小时前
用 Deepseek 写的html油耗计算器
前端·javascript·css·html·css3·deepseek