美文网首页
九、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