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

相关推荐
passerby60613 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了4 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅4 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅4 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅4 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment4 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅5 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊5 小时前
jwt介绍
前端
爱敲代码的小鱼5 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax
吹牛不交税5 小时前
admin.net-v2 框架使用笔记-netcore8.0/10.0版
vue.js·.netcore