ES6带了的Proxy代理机制,它提供了一些拦截操作:set、get、apply、has、ownKeys等,我们可以根据需求编写拦截程序,达到我们想要的效果;此外,还可以利用Proxy.revocable( )实现取消代理
Proxy的实现
get方法 拦截对象属性的读取
- target 代表存储对象
- key 字段名
- value 字段值
1
2
3
4
5
6
7
8
9
10
11
12
13//在业务开发中 作为原始对象存储数据 类似供应商的原始数据
let person = {
time:'2017-9-9',
name:'张三',
_r:123
};
let montor = new Proxy(person ,{ //代理 实现要代理的方法
//拦截对象属性的读取
get(target,key){
return target[key] = '李四'
}
}
onsole.log(montor.name)//2010-10-1 - 先定义一个对象,含有name属性,值为“张三”,创建一个代理对象montor,对象person的操作都交给代理对象montor,这不,看最后一句代码,如果你要读取person对象的name属性,就要用montor.name,而不是person的name。我们看到的结果是:“李四“而不是person对象重点张三,因为代理过程中,get方法实现了拦截的作用,不管你读取什么属性,我都返回”李四“
set方法
- set方法,它用于拦截对象设置属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41//定义一个对象,含有RMB和dollar属性值
var bankAccount = {"RMB":1000,"dollar":0};
//创建一个Proxy代理实例
var banker = new Proxy(bankAccount,{
//编写get处理程序
get:function(target, property){
//判断余额是否大于0
if(target[property] > 0){
//有余额,就返回余额值
return target[property];
}else{
//没钱了
return "余额不足";
}
},
//编写set处理程序
set:function(target,property,value){
//存入的数额必须是一个数字类型
if(!Number.isInteger(value)){
return "请设置正确的数值";
}
//修改属性的值
return target[property] = value;
}
});
banker.RMB;
//结果:1000
banker.dollar;
//结果:余额不足
//修改dollar属性的值,值是字符串类型
banker.dollar = "五百";
banker.dollar;
//结果:余额不足
//修改dollar属性的值,值是数字类型
banker.dollar = 500;
banker.dollar;
//结果:500 - 几乎每一句代码都有注释,这段代码对应的故事情节是这样的:老王有的银行账户里面有一些存款,其中人民币1000元,美元0元
1
var bankAccount = {"RMB":1000,"dollar":0};
- 有一天,他来到银行柜台前,找到一个叫banker的工作人员,取款之前看看账户里面还有多少钱,然后工作人员banker开始帮他操作(也就是代理)
1
2
3
4banker.RMB;
//结果:1000
banker.dollar;
//结果:余额不足 - banker告诉他:“您账户里面有人民币1000元,可以取款的,但美元余额不足
- 接着,老王不打算取款了,打算存500美元。
- 在填写存款单据的时候,把500不小心写成了“五百“,banker告诫老王:”这样是写不行的,一定要写阿拉伯数字,这样写银行无法帮您存款的“。结果存款失败,账户里面的美元还是0元
1
2
3banker.dollar = "五百";
banker.dollar;
//结果:余额不足 - 没关系,马上改过来,把“五百“改成500。
1
2
3banker.dollar = 500;
banker.dollar;
//结果:500ownKeys( )方法
- 拦截Object.keys, Object.getOwnPropertySymbols,Object.getOwnPropertyNames
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17//定义一个对象person,有三个属性
let person = {"name":"老王","age":40,"height":1.8};
//创建一个代理对象
let proxy = new Proxy(person,{
//ownKeys过滤对对象的属性遍历
ownKeys:function(target){
return ["name","age"]
}
});
Object.keys(person);
//结果:["name", "age","height"]
Object.keys(proxy);
//结果:["name", "age"] - 我们编写的ownKeys方法程序,不管你有多少属性,只返回两个属性name和age。我们看最后两行代码:Object.keys(person); 这里我们不使用代理,直接用keys( )函数遍历person对象,得到的person对象的原本属性”name”、 “age”和”height”。而Object.keys(proxy) 这句代码遍历的是被代理的proxy对象,所以,得到的只是被过滤后的结果:[“name”,”age”]
has( )方法
- 判断当前对象中是否拥有某个属性 拦截key in obj操作结果会返回一个布尔值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17var person = {
"name":"张三",
"age":20
};
var proxy = new Proxy(person, {
has: function(target, prop) {
if(target[prop] === undefined){
return false;
}else{
return true;
}
}
});
"name" in proxy;//结果:true
"height" in proxy;//结果:false - has( )方法用于是判断是否含有指定的键值对,有,就返回true。否则返回false
- 对象含有name属性,所以返回true,没有height属性,返回false
deleteProperty()方法
- 拦截 delete
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22let obj = {
time:'2017-9-9',
name:'net',
_r:123
};
let montor = new Proxy(obj,{ //代理
//拦截delete
deleteProperty(target,key){
if(key.indexOf('_')>-1){
delete target[key];
return true;
}else{
return target[key];
}
}
})
delete montor.time;
console.log('delete',montor)
//Proxy{time: "2017-9-9", name: "王磊",_r:123}//因为判断只删除带_的key
delete montor._r;
console.log('delete',montor)//Proxy{time: "2017-9-9", name: "王磊"}apply( )方法
- 除了对象类型的变量可以被代理,函数也可以被代理。如果被代理的变量是一个函数,那么还会支持一个拦截程序:apply调用。
1
2
3
4
5
6
7
8
9
10
11
12//创建一个函数fn
let fn = function(){
alert('我是前端君');
};
//创建一个代理实例,代理函数fn
let proxy = new Proxy(fn,{
apply:function(){
alert('我是隔壁老王');
}
});
proxy();//结果:我是隔壁老王 - 最后一句代码,proxy本身是一个代理实例对象,因为它代理的是一个函数fn,所以可以直接用函数的形式调用proxy( );当它当作函数调用的时候,就会被apply拦截,执行alert(‘我是隔壁老王’)
拦截方法总共包括:
- defineProperty( )
- deleteProperty( )
- enumerate( )
- getOwnPropertyDescriptor( )
- getPrototypeOf( )
- isExtensible( )
- preventExtensions( )
- setPrototypeOf( )
Reflect() 方法
- 跟Proxy的方法一摸一样 只是不用new
1
2
3
4
5
6
7
8
9
10let obj = { // 在业务开发中 作为原始对象存储数据 类似供应商的原始数据
time:'2017-9-9',
name:'net',
_r:123
};
//拿到值 和上边的用法名称都一样
console.log('Reflect get',Reflect.get(obj,'time '))
Reflect.set(obj,'name','job')
console.log(obj) //{time: "2017-9-9", name: "job", _r: 123}
console.log(Reflect.has(obj,'name'))个人练习
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60let obj = { // 在业务开发中 作为原始对象存储数据 类似供应商的原始数据
time:'2017-9-9',
name:'net',
_r:123
};
let montor = new Proxy(obj,{ //代理 实现要代理的方法
//拦截对象属性的读取
get(target,key){
return target[key] = '2010-10-10'
},
//拦截对象设置属性
set(target,key,value){
if(key = 'name'){ //只允许修改name
return target[key] = value
}else{
return target
}
},
//判断当前对象中是否拥有某个属性
//拦截key in obj操作
has(target,key){
//只暴露name属性
if(key === 'name'){
return target[key]
}else{
return false
}
},
//拦截delete
deleteProperty(target,key){
if(key.indexOf('_')>-1){
delete target[key];
return true;
}else{
return target[key];
}
},
//拦截Object.keys对对象的属性遍历。
ownKeys(target){
return Object.keys(target).filter(item => item!='time')
}
})
console.log('get',montor.time)//用户访问montor 在通过Proxy返回给obj
montor.time= '2019' //只允许修改name
montor.name= '王磊'
console.log('set',montor.time)
console.log('set',montor.name,montor)
console.log('has','name' in montor,'time' in montor)
//true false 因为只暴露name
delete montor.time;
console.log('delete',montor)
//Proxy {time: "2017-9-9", name: "王磊"}
delete montor._r;
console.log('delete',montor)
//Proxy {time: "2017-9-9", name: "王磊"}
console.log('ownKeys',Object.keys(montor))
//ownKeys["name", "_r"]开发环境 对数据进行校验
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37function validator(target,validator){
return new Proxy(target,{
_validator:validator,
set(target,key,value,proxy){
//判断当前目标对象是否有只值
if(target.hasOwnProperty(key)){
let va = this._validator[key];
if(!!va(value)){
return Reflect.set(target,key,value,proxy)
}else{
throw Error(`不能设置${key}到${value}`)
}
}else{
throw Error(`${key}不存早`)
}
}
})
}
const personValidator = {
name(val){
return typeof val === 'string'
},
age(val){
return typeof val === 'number' && val>18
}
}
class Person{
constructor(name,age){
this.name = name;
this.age = age
return validator(this,personValidator)//返回对this的代理
}
}
const person = new Person('lilei',30)
// person.name = 48;//报错
console.info(person)