因为之前在工作中没怎么用过Proxy,对他只是个简单的概念,最近准备面试,所以写这篇文章来恶补一下。
Proxy对象用于定义基本操作的自定义行为。这是MDN给的解释,挺绕口的是不是?简单来说,Proxy可以代理一个对象,并且自定义行为,在想要操作这个被代理对象时,是通过proxy对象去操作。
基本语法:
let proxy = new Proxy(target,handler)
target参数:需要被代理的对象
handler对象:此对象中定义了操作target的自定义方法
下面,还是以代码来解释:
我现在有这样的需要,有一个对象中有id,name,age,gender属性,我们需要对对象属性做出限制要求:id属性不可修改,age属性必须是Number类型
如果是未代理的方式,我们就得需要对原对象进行逻辑修改:
let obj = {
id:1,
name:'lisi',
age:18,
setValue:function(key,value){
if(key === 'id'){
console.error('id属性不允许被修改')
return
}else{
if(key == 'age' ){
if(typeof value === 'Number'){
obj[key] = value
}else{
console.error('age.value 必须是Number类型')
return
}
}
obj[key] = value
}
obj[key] = value
}
}
obj.setValue('id',2) // id属性不允许被修改
obj.setValue('age','20') // age.value 必须是Number类型
上述方法虽然可以解决,但是如果还有1个乃至更多对象有这个要求,难道要在每个对象中再写一遍这个逻辑??? 此时,我们就要用到Proxy代理实现了,代码入下
let target1 = {
id:1,
name:'lisi',
sex:20
}
let target2 = {
name:'',
id:2,
sex:0
}
let handler = {
set:function (target,key,value){
if(key === 'id'){
console.error('id属性不允许被修改')
return
}else{
if(key == 'age' ){
if(typeof value === 'Number'){
target[key] = value
}else{
console.error('age.value 必须是Number类型')
return
}
}
target[key] = value
}
target[key] = value
}
}
let proxy1 = new Proxy(target1,handler)
proxy.name = 'zs' // 修改成功
console.log(target1)
let proxy2 = new Proxy(target2,handler)
proxy2.name = 'lisi',
proxy2.age = 20,
proxy2.id = 2 // id属性不允许更改
要注意的是,handler中的方式名是不可以自定义的,而是Proxy提供的api。
Proxy中handler常用api:
-
handler.get()
属性读取操作的陷阱。 -
handler.set()
属性设置操作的陷阱。 -
handler.has()
-
handler.getPrototypeOf()
-
handler.setPrototypeOf()
-
handler.isExtensible()
-
handler.preventExtensions()
更多参考:Proxy用法
下面是一个常用api的例子代码:
{
// Proxy, 代理的就是对象的一些操作
let account = {
id: 9923,
name: 'admin',
_private: 'test',
phone: '13812345678',
create_time: '2019'
}
let accountProxy = new Proxy(account, {
// 拦截读取和设置的操作
get: function (target, key) {
switch (key) {
case 'phone':
return target[key].substring(0, 3) + '****' + target[key].substring(7)
case 'create_time':
return target[key].replace('2019', 2020)
default:
return target[key]
}
},
set: function (target, key, value) {
if (key === 'id') {
return target[key]
} else {
return target[key] = value
}
},
// d
has: function(target, key) {
if(key in target) {
console.log(`${key}:`, target[key])
return true
} else {
console.log('并无此属性')
return false
}
},
// 拦截delete
deleteProperty: function(target, key) {
if(key.indexOf('_') === 0) {
console.warn('私有属性不能被删除')
return false
} else {
delete target[key]
return true
}
},
// 拦截Object.keys()
ownKeys(target) {
return Object.keys(target).filter(function(item) {
return item !== 'id' && item.indexOf('_') !== 0
})
}
})
console.log('拦截读取', accountProxy.phone, accountProxy.create_time)
accountProxy.id = 1234
accountProxy.name = 'guest'
console.log('拦截设置', accountProxy.id, accountProxy.name)
console.log('====')
console.log('拦截in', 'sex' in accountProxy)
console.log('====')
console.log('拦截删除', delete accountProxy['_private'])
console.log('拦截Object.keys()',Object.keys(accountProxy))
}
{
let obj = {
name: 'Nick',
age: '32',
sex: 'male',
hobbies: 'swimming'
}
console.log(Reflect.get(obj, 'name'))
Reflect.set(obj,'name', 'Jack')
console.log(obj.name)
'name' in obj
Reflect.has(obj, 'name')
}
Reflect用法和Proxy的handler类似,但是Reflect是js的内置对象,不可构建,使用方法比较简单
let obj = {
name:'李四',
age:20
}
Reflect.set(obj,'name','张三') // true,修改成功返回true
console.log(obj) // {name:'张三',age:20}
这里也有个坑要注意:对象key名要用引号包裹,比如上面obj的name属性,那么对这个值进行修改,就是
Reflect.set(obj,‘name’,‘要修改的值’)
Reflect常用方法:
- Reflect.set()
- Reflect.get()
- Reflect.has()
更多用法参考:Reflect常用方法
水平有限,这里只做基本使用的方法,仅作记录用。不喜勿喷
推荐大佬关于Proxy的解析:[Proxy解析](https://www.jianshu.com/p/c2a1aa2e2b14)
最后附上Proxy和Reflect的兼容性图示:
Proxy:
Reflect:
IE浏览器是不支持Proxy和Reflect的