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

相关推荐
无限大615 分钟前
为什么计算机要使用二进制?——从算盘到晶体管的数字革命
前端·后端·架构
谎言西西里18 分钟前
从模板渲染到响应式驱动:前端崛起的技术演进之路
vue.js
良木林25 分钟前
字节前端高频面试题试析
前端
一 乐27 分钟前
家政管理|基于SprinBoot+vue的家政服务管理平台(源码+数据库+文档)
前端·javascript·数据库·vue.js·spring boot
fruge31 分钟前
图片优化终极指南:WebP/AVIF 选型、懒加载与 CDN 配置
前端
掘金一周33 分钟前
数据标注平台正式上线啦! 标注赚现金,低门槛真收益 | 掘金一周 12.10
前端·人工智能·后端
Macbethad39 分钟前
工业触摸屏技术指南:选型、难点与实战解决方案
服务器·前端·数据库
桦说编程42 分钟前
ConcurrentHashMap 弱一致性解读
后端·性能优化·源码
源码获取_wx:Fegn089542 分钟前
基于springboot + vue停车场管理系统
java·vue.js·spring boot·后端·spring·课程设计
cc蒲公英43 分钟前
vue 对象、数组增删改,对比vue2和vue3 —— 最新总结2025
前端·javascript·vue.js