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

相关推荐
风之舞_yjf17 分钟前
Vue基础(14)_列表过滤、列表排序
前端·javascript·vue.js
BillKu1 小时前
scss(sass)中 & 的使用说明
前端·sass·scss
疯狂的沙粒1 小时前
uni-app 项目支持 vue 3.0 详解及版本升级方案?
前端·vue.js·uni-app
Jiaberrr1 小时前
uniapp Vue2 获取电量的独家方法:绕过官方插件限制
前端·javascript·uni-app·plus·电量
Lhuu(重开版1 小时前
Vue:Ajax
vue.js·ajax·okhttp
谢尔登2 小时前
【React】React 18 并发特性
前端·react.js·前端框架
Joker`s smile2 小时前
使用React+ant Table 实现 表格无限循环滚动播放
前端·javascript·react.js
国家不保护废物2 小时前
🌟 React 魔法学院入学指南:从零构建你的第一个魔法阵(项目)!
前端·react.js·架构
import_random2 小时前
[机器学习]svm支持向量机(优势在哪里)
前端
国家不保护废物2 小时前
从刀耕火种到现代框架:DOM编程 vs Vue/React 进化史
前端·vue.js·react.js