2023-02-02
proxy 对象
前言
现在vue3.0虽然没有大规模使用在项目中,但面试的时候问的可一点也不少.特别是vue中双向数据绑定,几乎必问.但vue2.0和vue3.0的数据绑定还是不一样的....
一、vue2.x的双向数据绑定
· 在Vue2.x中,双向数据绑定是通过Object.definePropert实现的.
· 用get,set方法中通过发布订阅者模式来实现的数据响应。
· 但它只能监听存在的属性,对于新增或删除的便无能无力.
· 同时数组的改变他也是没有办法监听的.
二、proxy的使用
1.proxy是个什么玩意
· ES6中新增的代理反射,可以然让我们拦截一些操作,且添加其它的能力.
· 简单说,我们对某个对象设置拦截器后,它的一些操作就能被我们捕获到,如此我们可以按照需求对默认的行为进行修改或者其它操作.
· 类似于常见的钩子函数,发生某个行为时,触发某个钩子函数.
2.基本形式
语法
const proxy = new Proxy(target, handler)
· target: 要监听的对象,可以是一个对象,数组,函数等等
· handler: 是对象,里面包含了可以监听target的方法.
· proxy为返回的新对象, 为了能够触发handler里面的函数,必须要使用返回值去进行其他操作,比如修改值.handler里面的方法可以有以下这十三个,每一个都对应的一种或多种针对proxy代理对象的操作行为
支持的写法
Proxy提供了十三种拦截对象操作的方法,这里只介绍几个vue3中常用到的,更多的请参考MDN
1. handler.get()当通过proxy去读取对象里面的属性的时候,会进入到get钩子函数里面.
2. handler.set当通过proxy去为对象设置修改属性的时候,会进入到set钩子函数里面
3. handler.has当使用in判断属性是否在proxy代理对象里面时,会触发has,比如
const obj = {
name: '小甜甜'
}
console.log('name' in obj)
3. handler.apply当proxy监听的是一个函数的时候,当调用这个函数时,会进入apply钩子函数
4. handler.construct当使用new操作符的时候,会进入construct这个钩子函数
5. handler.defineProperty当使用Object.defineProperty去修改属性修饰符的时候,会进入这个钩子函数
3.常用API
Reflect 对象
const target = {
id:'target'
}
const handler ={
// 捕获器在处理程序时,以方法名为键
get(){
return 'handle override';
}
}
const proxy = new Proxy(target,handler);
console.log(target.id); // target
console.log(proxy.id); // handle override
· 以上方法中,是基于参数,重建操作,可不是所有的捕获器行为都像get这么简单
· 我们可以通过Reflect对象上的同名方法,来继续当前的默认行为.
const target = {
id:'target'
}
const handler ={
// 捕获器在处理程序时,以方法名为键
get(){
//Reflect上的同名方法 继续原来的操作
return Reflect.get(...arguments);
}
}
const proxy = new Proxy(target,handler);
console.log(target.id); // target
console.log(proxy.id); // target
· 操作符的替代
Reflect.get() 替代对象属性访问操作符号.
Reflect.set() 替代= 赋值操作符,返回 bool值
Reflect.has() 替代in操作符或者with()
Reflect.deleteProperty() 替代delete操作符
Reflect.constructor() 替代new操作符
代理捕获器和反射方法
对于代理对象上的任何一种操作,只会有一个捕获处理器被调用.
1. get() 在获取属性值,操作时调用.对应反射API为Reflect.get()
const target = {
id:'target'
}
const handler ={
// 捕获器在处理程序时,以方法名为键
get(){
console.log('get()被调用');
return Reflect.get(...arguments);
}
}
const proxy = new Proxy(target,handler);
proxy.foo; // get()被调用
2. set() 捕获器会在设置属性时调用,对应反射API为Reflect.set()
const target = {
id:'target'
}
const handler ={
// 捕获器在处理程序时,以方法名为键
set(target,property,value,receiver){
console.log('set()被调用');
return Reflect.set(...arguments);
}
}
const proxy = new Proxy(target,handler);
proxy.foo = 'bar';
//target 为目标对象
// property 为属性
// value 为属性值
// receiver 接收最初赋值对象
· apply() 会在调用函数时被调用,对应的反射api为Reflect.apply()
const fn = ()=>{
}
const proxy = new Proxy(fn,{
apply(target,thisArg,...argumentsList){
console.log('apply()');
return Reflect.apply(...arguments)
}
})
proxy(); // apply
· construct() 会在new操作符时被调用;
const fn = function(){
}
const proxy = new Proxy(fn,{
// target必须为构造函数
construct(target,argumentList,newTarget){
console.log('construct');
return Reflect.construct(...arguments)
}
})
new proxy();
4.使用场景
a. 隐藏属性,
将目标对象上的属性进行隐藏.
const hiddenProperites = ['foo','bar'];
const tarObject = {
foo:1,
bar:2,
baz:3
};
const proxy = new Proxy(tarObject,{
get(target,property){
if(hiddenProperites.includes(property)){
return undefined;
}else{
return Reflect.get(...arguments)
}
},
has(target,property){
if(hiddenProperites.includes(property)){
return false;
}else{
return Reflect.has(...arguments)
}
}
});
console.log(proxy.foo); // undefined
console.log(proxy.baz); // 3
console.log('foo' in proxy); // false
console.log('baz' in proxy); // true
b 属性验证
所有的赋值操作都会触发set,我们可以所赋的值决定是允许还是拒绝.
const target = {
num:0
}
const proxy = new Proxy(target,{
set(target,property,value){
// 是number 类型才允许赋值
if(typeof value !== 'number'){
return false;
}else{
return Reflect.set(...arguments)
}
}
});
proxy.num = 2;
console.log(proxy); // Proxy {num: 2}
proxy.num = 'abc';
console.log(proxy); // Proxy {num: 2}
c 构造函数必须传参
和保护和验证对象的属性相似,我们也可以对构造函数的参数进行审查.
// 类必须传递参数
class User{
constructor(id) {
this._id = id;
}
}
const proxy = new Proxy(User,{
construct(target,argumentsList,newTarget){
if(argumentsList[0]===undefined){
throw '必须传递参数'
}else{
return Reflect.construct(...arguments);
}
}
})
new proxy(); // Uncaught 必须传递参数
d 对象的可观察
向数组中每插入一个值,emit方法就会收到消息
const userList = [];
// 接收传递的值
function emit(newValue){
console.log(newValue);
}
const proxy = new Proxy(userList,{
set(target,property,value,receiver){
const result = Reflect.set(...arguments)
if(result){
// 设置值时,调用emit方法
emit(Reflect.get(target,property,receiver));
}
return result;
}
});
proxy.push('小甜甜');
三、总结
· 代理是由你创建的特殊对象,它封装另一个普通对象,或者谁挡在普通对象前面.
· 可以在代理对象上注册特殊的处理函数,代理商执行各种操作时就会调用这个程序.
· 这些程序除了把操作转发给原始目标/被封对象外,还会执行其它额外操作.
上一篇:Sitespeed使用教程
下一篇:Hive的分区表
开班时间:2021-04-12(深圳)
开班盛况开班时间:2021-05-17(北京)
开班盛况开班时间:2021-03-22(杭州)
开班盛况开班时间:2021-04-26(北京)
开班盛况开班时间:2021-05-10(北京)
开班盛况开班时间:2021-02-22(北京)
开班盛况开班时间:2021-07-12(北京)
预约报名开班时间:2020-09-21(上海)
开班盛况开班时间:2021-07-12(北京)
预约报名开班时间:2019-07-22(北京)
开班盛况Copyright 2011-2023 北京千锋互联科技有限公司 .All Right 京ICP备12003911号-5 京公网安备 11010802035720号