Vue.js设计与实现-Proxy和Reflect

1.写在前面

2.Proxy代理

Proxy代理就是通过Proxy对一个原始对象进行基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。

创新互联建站是一家专注于网站设计、做网站与策划设计,武城网站建设哪家好?创新互联建站做网站,专注于网站建设10余年,网设计领域的专业建站公司;建站业务涵盖:武城等地区。武城做网站价格咨询:13518219792

const p = new Proxy(target, handler);

在上面代码片段中,Proxy可以接收两个参数target、handler:

  • target:表示要进行代理的原始对象(可以是任意类型对象,函数、数组等)。
  • handler:通常以函数作为属性的对象,该对象是一组夹子(trap),各属性中的函数分别定义了在执行各种操作时代理 p 的行为。

那么接下来,我们就来使用下吧。

const data = {
name:"pingping",
age:18
}

const state = new Proxy(data, {
//拦截属性取值操作
get(target, key){
//在这里拦截打印数据
console.log(`我的${key}是:${target[key]}`);
return target[key];
},
//拦截属性设置操作
set(target, key, value){
//在这里拦截设置数据
target[key] = value;
console.log(`我的${key}数据更改为${target[key]}`);
return value
}
});

state.name;

state.name = "onechuan"

打印数据为:

控制台打印数据

在上面代码中,我们只用到了get的前两个参数target和target,分别表示代理的原始对象、被获取的属性名。其实Proxy的get方法中还可以传入第三个参数receiver,表示Proxy代理之后的对象或继承Proxy的对象。

  • target:被代理的原始对象。
  • property:被获取的属性名。
  • receiver:Proxy代理后的对象或者继承Proxy的对象。
const p = new Proxy(target, {
get: function(target, property, receiver) {
}
});

我们进行个简单的实践:

const data = {
name:"pingping",
age:18
}

const state = new Proxy(data, {
get: function(target, property, receiver) {
console.log(state === receiver);
return target[property];
}
});

state.name;

此时,看到控制台打印的结果是:

控制台打印结果

在上面举的例子中,的确receiver指的是Proxy代理之后的对象state,当然receiver也可以指向的是继承Proxy的对象。

const data = {
name:"pingping",
age:18
}

const state = new Proxy(data, {
get: function(target, property, receiver) {
console.log(state === receiver);
return target[property];
}
});

const obj = {
name:"onechuan"
}

//将obj对象原型设置为state对象,即obj继承state
Object.setPrototypeOf(obj, state);

obj.name;

控制台打印结果:

设置继承的打印结果

在上述代码和打印结果中,我们可以看到打印的receiver并不等于state对象。这是因为get方法的第三个参数receiver,可以传递对象get调用者指向,即可以正确传递上下文。

receiver不仅表示Proxy代理后的对象state本身,也会表示继承Proxy的对象。

const data = {
name:"pingping",
age:18,
get value(){
return this.name;
}
}

const state = new Proxy(data, {
get: function(target, property, receiver) {
console.log(this === receiver);//false
console.log(state === receiver);//false
console.log(obj === receiver);//true
return target[property];
}
});

const obj = {
name:"onechuan"
}

//将obj对象原型设置为state对象,即obj继承state
Object.setPrototypeOf(obj, state);

obj.value;

我们看到控制台的打印结果如下:

控制台打印结果

我们分析下上面代码,在访问obj.value时,在obj对象上本身是没有value属性的,它会通过原型去查找proxy对象state上的value属性取值器,会去触发state对象上的get value()操作符。此时,会触发state对象的取值拦截器,返回target[property]的值,这样使用obj.value取值就变成了data.value,最终返回的结果就是pingping。

具体的Proxy的一些API见MDN文档吧。

3.Reflect反射

在了解了Proxy后,我们再来讨论下Proxy的好兄弟Reflect,它们是如何配合工作的。

Reflect是一个全局内置的对象,它提供拦截JavaScript操作的方法。但是,Reflect本身不是个函数对象,因此其不是一个构造函数,不能使用new进行调用。Reflect的所有属性和方法都是静态的。

Reflect.get(target, propertyKey[, receiver])
  • target:需要取值的目标对象
  • propertyKey:需要获取的值的键值
  • receiver:如果target对象中指定了getter,receiver则为getter调用时的this值

那么,我们就使用Reflect来获取对象的属性吧:

const state = new Proxy(data, {
get: function(target, property, receiver) {
console.log(this === receiver);//false
console.log(state === receiver);//false
console.log(obj === receiver);//true
return Reflect.get(target, property);
// 等价于return target[property];
}
});

在控制台可以看到,打印结果是一样的:

控制台打印结果

在这里,使用Reflect.get(target, property)是等价于return target[property]的,this在Proxy.get拦截器中将this的指向了原始数据data对象,这样obj.value打印结果自然也是pingping。

如果我们要获取obj对象自身的name属性,应该怎么办?

onst state = new Proxy(data, {
get: function(target, property, receiver) {
console.log(this === receiver);//false
console.log(state === receiver);//false
console.log(obj === receiver);//true
return Reflect.get(target, property, receiver);
// 等价于return target[property];
}
});

打印结果:

打印结果

我们将Proxy.get的第三个参数receiver传入Reflect.get中,此时我们发现打印结果就变成了onechuan。这是因为Proxy.get的第三个参数receiver,可以表示代理对象state还可以表示继承代理对象state的对象obj。而在Reflect.get中传入了Proxy.get的第三个参数receiver,即obj对象作为参数,此时Reflect.get会把this的指向改为obj。

Reflect.get(target, key, receiver)其实可以理解为target[key].call(receiver),而Reflect.get的参数receiver作用:修改属性访问时this的指向receiver。

4.参考文章

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

《MDN文档关于Proxy的描述》

《MDN文档关于Reflect的描述》

《了解学习 Proxy 的好朋友 - Reflect,为什么需要 Reflect》

5.写在最后

在Vue.js3中使用Proxy来实现响应式数据,具体就是通过Proxy代理原始对象,通过拦截和修改对象的基本操作。在代理过程中,会出现取值器的this指向问题,此时需要使用Reflect的方法第三个参数receiver来解决。

分享名称:Vue.js设计与实现-Proxy和Reflect
网站网址:http://www.csdahua.cn/qtweb/news0/484700.html

网站建设、网络推广公司-快上网,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 快上网