Proxy的意义
proxy相当于对象的一个替身,我们可以直接对对象进行操作也可以 通过proxy代理对对象进行操作。在proxy中可以设定一些拦截器来对原本默认的一些操作进行复写从而对操作起到过滤作用。
虽然Proxy实例也是用new操作符和构造函数生成的,但是他的prototype是undefined
代理的途径——捕获器(拦截器)
ES5中拦截的做法——Object.defineProperty()/Object.defineProperties()
这两个方法可以对属性进行配置、拦截。前者有三个参数(对象,属性名,配置信息对象),后者两个参数(对象,一个包含所有属性配置的对象)
配置信息对象有以下六个属性:
- Configurable 是否可通过delete删除,默认为true
- Enumberable 是否可用for in遍历出来,默认为true
- Writable 是否可写,默认为true
- Value 属性的值,默认为undefined
- Get 获取函数,默认为undefined
- Set 设置函数,默认为undefined
let obj = {};
let newVal = '';
Object.defineProperty(obj,prop,{
//对于拦截方法的配置
get(){
return newVal;
},
set(val){
newVal = val;
},
})
通过对比可以发现,ES5中的写法是对直接读取对象上的属性进行拦截,而Proxy是通过一个代理对象。ES5中对属性的拦截需要一个个设置,Proxy则可以一次性针对所有属性进行判断。(所以 Vue2无法让新属性(就是不在data里声明,而是通过vue实例使用vm.p声明的属性)有响应效果,但是Vue3可以)
要通过代理对象操作才会触发捕获器,直接操作目标对象会绕过捕获器。
let proxy = new Proxy(target,handle)
第一个参数是代理的对象,第二个参数是捕获器的对象。二者都不能为空,但可以是空对象。
Proxy中常用的拦截方法:
get(两个参数)报错或返回值
let arr = [7,8,9];
arr = new Proxy(arr,{
get(target,prop){//target指向数组,prop指向下标
return prop in target? target[prop]:'error';
}
}
let dict = {
'hello':'你好'
'world':'世界'
}
dic = new Proxy(dic,{
get(target,prop){
return prop in target? target[prop]:prop;
}
})
set(三个参数)报错或返回波尔型的值
let arr = [];
arr = new Proxy(arr,{
set(target,prop,val){
if(typeof val === 'number'{
target[prop] = val;
return true;
}else {
return false;
}
}
})
has(两个参数)返回波尔型的值
let range = {
start:1,
end:5
}
range = new Proxy(range,{
has(target,prop){
return prop >=target.start && prop <= target.end;
}
})
ownKeys(一个参数)返回一个数组
在Object.keys以及类似的遍历方法中被调用
以下是之前学习过的循环遍历,对于遍历的值他们会有一些过滤。
let obj = {
name:'imooc',
[Symbol('es')]:'es6'
}
Object.getOwnPropertyNames(obj); //输出name
Object.getOwnPropertySymbolss(obj); //输出Symbol(es)
使用Propx:
使用以下代码,可以对遍历的量进行一些控制。比如下面的代码就实现了不遍历‘_’开头的密码。
let userinfo = {
username:'CLeon',
age:22,
_password:******
}
userinfo = new Proxy(userinfo,{
ownKeys(target){
return Object.keys(target).filter(key => !key.startWith('_');
}
})
deleteProperty(两个参数)报错或返回波尔型的值
delete操作符中被调用
deleteProperty(target,prop){}
apply拦截函数的调用、call和apply操作(三个参数,分别是目标对象、目标对象的上下文对象(this)和目标对象的参数数组),当函数被调用时,可以修改函数的返回值。
apply(target,context,args){}
return target(...args);
construct拦截new命令(三个参数),返回一个对象
construct()方法返回的必须是一个对象,否则会报错。
construct(target,args,newTarget){//当前目标对象,当前构造函数的参数列表,创建实例时new作用的
}
反射Reflect
Reflect的设计有四个目的
- 将Object上的方法都移到Reflect上,并且未来新方法也只在Reflect上了。
- 修改某些Object方法的返回结果,让其变得更合理。执行失败不会抛错了,而是返回一个false。这样就不用try了,直接判断就好了。
- 让Object操作都变成函数行为。某些Object操作是命令式,比如name in obj和delete obj[name],而Reflect.has(obj, name)和Reflect.deleteProperty(obj, name)让它们变成了函数行为。
- Reflect对象的方法与Proxy对象的方法一一对应,只要是Proxy对象的方法,就能在Reflect对象上找到对应的方法。
针对第四点,Reflect和Proxy的对应,相应的方法会实现相应的操作
这里的receiver指的就是Proxy对象,感觉一般也用不着不写也行。
如果不涉及对个别参数的操作,所有对应的方法,参数都可以用(…arguments)来替代。
1、Reflect.get(target, name, receiver)
Reflect.get方法查找并返回target对象的name属性,如果没有该属性,则返回undefined。
return target[name] => return Reflect.get(target.name)
=> return Reflect.get(...arguments)
2、Reflect.set(target, name, value, receiver)
Reflect.set方法设置target对象的name属性等于value。
在这里插入代码片
3、Reflect.has(obj, name)
Reflect.has方法对应name in obj里面的in运算符。
4、Reflect.deleteProperty(obj, name)
Reflect.deleteProperty方法等同于delete obj[name],用于删除对象的属性。
5、Reflect.construct(target, args)
Reflect.construct方法等同于new target(…args),这提供了一种不使用new,来调用构造函数的方法。
6、Reflect.apply(func, thisArg, args)
Reflect.apply方法等同于Function.prototype.apply.call(func, thisArg, args),用于绑定this对象后执行给定函数。