defineProperty
回顾es5中,监听对象属性的修改和获取等操作,用defineProperty
function watch(obj,key,value) {
Object.defineProperty(obj,key, {
get() {
// console.log('get')
return value
},
set(newval) {
// console.log('set', newval)
if (newval !== value) {
value = newval;
}
}
})
}
let obj = {};
watch(obj,'name'); //监听obj的name属性
console.log(obj.name) //undefined
obj.name = 'della'
console.log(obj.name) //della
Proxy代理
let obj = {}
let p = new Proxy(obj,{}) //代理给p
p.name = 'aaaaa' //修改p相当 于修改了obj
console.log(p,obj)
- 数组代理,拦截数组元素的读取get和设置set
let arr = [1,4,5,7];
arr = new Proxy(arr,{
get(target,prop){//target监听的数组
return prop in target ? target[prop] : 'error'
},
set(target,prop,val){ //拦截数组元素只能设置数值
if(typeof val === 'number'){
target[prop] = val
return true //表示成功
}else{
// return false
throw new TypeError('The '+val+' seems not number');
}
}
})
console.log(arr[6]) //error //触发get
arr[1] = 26; //触发set
arr.push(99) //触发set
console.log(arr) //触发get
- 用in关键字判断对象/数组是否包含某个属性/元素时,会触发has方法
let range = {
start :1,
end :5
}
range = new Proxy(range,{
has(target,prop){ //target监听的对象
//console.log(target,prop)
return prop >= target.start&&prop<=target.end
}
})
console.log(1 in range) //in判断会触发has
3、对象代理,'_'开头的属性不允许删除deleteProperty,读取get,修改set和遍历ownKeys
let userInfo = {
name:'della',
age:18,
_password:'1234',//不希望被操作的属性
}
userInfo = new Proxy(userInfo,{
ownKeys(target){ //拦截遍历操作
// 当对象被遍历时,只返回不以'_'开头的key
return Object.keys(target).filter(key => !key.startsWith('_'))
},
get(target,prop){ // 拦截读取操作
if(prop.startsWith('_')){
throw new Error('不能访问')
}else{
return target[prop]
}
},
set(target,prop,val){ //拦截设置操作
if(prop.startsWith('_')){
throw new Error('不能访问')
}else{
target[prop] = val; //执行设置操作
return true
}
},
deleteProperty(target,prop){ //拦截删除操作
if(prop.startsWith('_')){
throw new Error('不能访问')
}else{
delete target[prop] //执行删除操作
return true //返回 布尔值表示操作成功
}
}
})
for(let key in userInfo){ //遍历对象属性时,会触发ownKeys方法
console.log(key)
}
console.log(userInfo.name)//读取时会触发get
userInfo.name = 'aaaa'//设置时会触发set
delete userInfo.age //删除时会触发deleteProperty
console.log(userInfo)
- 函数代理,函数调用时进行拦截,自动触发apply方法
let sum = (...args)=>{
let num = 0;
args.forEach(item=>{
num+=item
})
return num
}
sum = new Proxy(sum,{ //对sum函数进行拦截处理
//当函数被调用时,会触发apply方法
apply(target,ctx,args){//args是所有参数组成的数组。ctx为要改变的目标指向,如call和apply方式调用函数时,就是传入的第一个参数
console.log(ctx)
return target(...args)*2 //调用时args要展开。调用时*2做点改变代表进入过这里
}
})
console.log(sum(1,2)) //6 常规调用
console.log(sum.call(null,1,2,3)) //12 call调用
console.log(sum.apply(null,[1,2,3])) //12 apply调用
- 类代理,new实例化对象时进行拦截,自动触发constructor方法
class User{ //声明一个User类
constructor(name){
this.name = name
}
}
User = new Proxy(User,{
//new时会触发construct方法
construct(target,args,newTarget){
console.log(1)//new时会被打印
return new target(...args)
}
})
console.log(new User('aaaas'))
以上提到的常用拦截方法:
- get :拦截对象属性/数组元素的读取
- set:拦截对象属性/数组元素的设置
- has:拦截propKey in proxy的操作,返回一个布尔值
- ownKeys :拦截Object.keys(proxy)、Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、for...in...循环,返回一个数组。
- deleteProperty 拦截对象删除属性操作
- apply: 拦截函数调用、call和apply操作
- construct: 拦截new命令,返回一个实例对象
Proxy对比Object.defineProperty的优势
defineProperty无法一次性监听对象的每个属性,必须便利或递归。
defineProperty无法监听新增属性。
defineProperty不支持数组监听,对于数组,要劫持数组方法,进行函数劫持。
以上Proxy都支持。
网友评论