美文网首页
九、Proxy代理

九、Proxy代理

作者: della岳 | 来源:发表于2022-02-10 09:10 被阅读0次

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)
  1. 数组代理,拦截数组元素的读取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
  1. 用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)
  1. 函数代理,函数调用时进行拦截,自动触发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调用
  1. 类代理,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都支持。

相关文章

网友评论

      本文标题:九、Proxy代理

      本文链接:https://www.haomeiwen.com/subject/xxzwkrtx.html