ES6笔记 - proxy与Reflect简介

Proxy与Reflect

1. Proxy

1.1 Proxy简介

  • Proxy可以理解为在目标对象前架设一个"拦截层",外界对该对象的访问都必须先通过这层拦截,因此提供了一种机制可以对外界的访问进行过滤和改写

  • 语法:var proxy = new Proxy(target, handler)

    • target参数表示所要拦截的目标对象,即如果没有Proxy介入,操作原来要访问的就是这个对象
    • handler参数是一个对象,用来定义拦截行为。Proxy会根据拦截行为重载方法,即用自己的定义覆盖语言的原始定义
    //此处拦截目标对象是一个空对象
    var obj = new Proxy({}, {
       //拦截并重载读取操作
       get:function(target, key, receiver){
           console.log('getted!')
           //拦截并处理完成,使用Reflect中的原生get方法
           return Reflect.get(target, key, receiver);
       },
       //拦截并重载写入操作
       set:function(target, key, value, receiver){
           console.log('setted!')
           //拦截并处理完成,使用Reflect中的原生set方法
           return Reflect.set(target, key, value, receiver);
       }
    });
    
    obj.count = 1;	//setted!
    ++obj.count;	//getted! setted!
    
  • 如果要使拦截生效,必须对Proxy实例对象操作,而不是对目标对象操作。对Proxy对象的操作会影响目标对象,对目标对象的操作却不会触发Proxy的拦截

1.2 注意事项

  • Proxy不一定到代理某一个目标对象,它本身也可以作为一个具有拦截操作的对象。如果不想代理某个对象,只要把target设置为空对象{}就可以了

  • 如果一个属性不可配置(configurable)或不可写(writable),那么该属性不能被代理,通过Proxy对象访问或修改该属性均会报错

  • proxy代理可能会产生this指向问题,因为在Proxy代理的情况下,目标对象的this关键字会指向Proxy代理

    const target = {
        m.function(){
            console,log(this === proxy);
        }
    };
    const handler = {};
    
    const proxy = new Proxy(target, handler);
    
    target.m();	//false
    proxy.m();	//true
    

    甚至有些原生对象的内部属性只有通过争取的this才能获取,此时使用proxy也无法代理这些原生对象的属性

    const target = new Date();
    const handler = {};
    const proxy = new Proxy(target, handler);
    
    proxy.getDate();
    

1.3 Proxy支持的所有拦截操作

出于减少记忆成本的目的,除特殊几个拦截操作,其余拦截操作不一一列出,只要记住规律即可:

所拦截的操作大都是Object对象中的同名操作,如getOwnPropertyDescriptor(target, propKey)所拦截的就是Object.getOwnPropertyDescriptor(proxy,propKey)。所以只要看到拦截操作名称就能大致知道所拦截的是什么操作

所有拦截操作

  • get(target, propKey, receiver)

    • 拦截对象属性的读取,propKey表示被读取的属性,receiver是一个可选对象
  • set(target, propKey, receiver)

    • 拦截对象属性的设置,返回一个布尔值
  • has(target, propKey)

    • 拦截propKey in proxy的操作,返回一个布尔值
  • deleteProperty(target, propKey)

    • 拦截delete proxy[propKey]
  • getOwnPropertyDescriptor(target, propKey):返回属性的描述对象

  • preventExtensions(target):返回布尔值

  • getPrototypeOf(target):返回对象

  • isExtensible(target):返回布尔值

  • setPrototypeOf(target, proto):返回布尔值

  • defineProperty(target, propKey, propDesc)

    • 拦截Object.defineProperty(proxy, propKey, propDesc)
    • 拦截Object.defineProperties(proxy,propDescs)
    • 返回布尔值
  • ownKeys(target)

    • 拦截Object.getOwnPropertyNames(proxy)
    • 拦截Object.getOwnPropertySymbols(proxy)
    • 拦截Object.keys(proxy)
    • 返回一个数组,包含目标对象所有自身属性的属性名

如果目标对象是函数,那么还有两种额外操作可以拦截

  • apply(target, object, args)
    • 拦截proxy实例,并将其作为构造函数调用的操作,比如proxy(...args)proxy.call(object, ...args)
  • construct(target, args)
    • 拦截Proxy实例作为构造函数调用的操作,比如new proxy(...args)

1.4 使用示例

  • 如果访问目标对象不存在的属性,抛出一个错误而不是单纯的返回undefined

    var person = {
        name: "张三"
    };
    
    var proxy = new Proxy(person,{
        get:function(target,property){
           //如果存在,就原样返回值
           if(property in target){
               return target[property]
           }
           else{
               throw new ReferenceError("Property \"" + property + "\" dose not exist");
           }
       }
    })
    
    proxy.name;	//"张三"
    proxy.age;	//抛出错误
    
  • 保证Person对象中的age属性是一个不大于200的整数

    let handler = {
        set: function(obj, prop, value){
            if(prop === 'age'){
                if(!Number.isInteger(value)){
                    throw new TypeError('The age is not an integer');
                }
                if(value > 200){
                    throw new RangeError('The age is invaild');
                }
            }
            
            obj[prop] = value;
        }
    };
    
    let person = new Proxy({},handler);
    
    person.age = 100;
    person.age = 'hi';	//报错
    person.age = 200;	//报错
    

2. Reflect

  • Reflect的设计目的主要有以下几个:

    • 将Object对象上的一些应该属于语言内部的方法转移到Reflect对象上,如:

      Object.defineProperty() => Reflect.defineProperty()

    • 修改某些Object方法的返回结果,使其变得更加合理,如:

      Object.defineProperty()在无法定义属性时会抛出一个错误

      Reflect.defineProperty()则会返回false

    • 让指令式行为的Object操作变为函数行为,如:

      Object中name in objdelete obj[name]

      Reflect中Reflect.has(obj,name)Reflect.deleteProperty(obj,name)

    • Reflect对象的方法与Proxy对象的方法一一对应,只要是Proxy对象的方法,都能在Reflect对象上找到对应的方法。这就使得Proxy对象可以方便的调用对应的Reflect方法来完成默认行为

  • 总的来说Reflect是和Proxy配套使用的,修改了Object默认方法的一个API,本身并没有太多需要记忆的地方,只需要对照Proxy中的方法使用即可

posted @ 2022-12-07 17:53  Solitary-Rhyme  阅读(77)  评论(0编辑  收藏  举报