美文网首页
ES6 之 Proxy 和 Reflect

ES6 之 Proxy 和 Reflect

作者: 泉泉泉泉泉泉 | 来源:发表于2019-06-17 16:00 被阅读0次

    Proxy

    在教程中对于Proxy的定义是这样的,Proxy用于修改某些操作的默认行为,即对编程语言层面进行修改,属于“元编程”,Proxy意思为“代理”,即在访问对象之前建立一道“拦截”,任何访问该对象的操作之前都会通过这道“拦截”,即执行Proxy里面定义的方法。

    举个栗子:

    var obj = { time: '2018-01-01', name: 'lx', _r: 123 }
    var monitor = new Proxy(obj, {
      get(target, key) {  //读取
        return target[key] && target[key].replace('2018', '2017')  //把值的2018改成2017
      },
      set(target, key, value) {  //设置
        if (key === 'name') { return target[key] = value }  //只允许修改name
        else { return target[key] }
      },
      has(target, key) {  //拦截key in object
        if (key === 'name') { return target[key] }
        else { return false }
      },
      deleteProperty(target, key) {  //拦截delete
        if (key.indexOf('_') > -1) { delete target[key]; return true }
        else { target[key] }
      },
      ownKeys(target) {//拦截Object.keys,Objects.getOwnPropertySymbols,Object.getOwnPropertyNames等
        return Object.keys(target).filter(item => item != 'time')
      }
    })
    //用户看到和操作的是monitor
    console.log('get', monitor.time)   //2017-01-01
    monitor.time = '2019'
    console.log('set', monitor.time)  //2017-01-01
    console.log("monitor",monitor)
    console.log('has', 'name' in monitor, 'time' in monitor) //true  false
    delete monitor.time
    console.log('delete', monitor)  //time依然存在于monitor
    console.log('ownkeys', Object.keys(monitor));  //,保护monitor不显示time
    

    猜猜应该输出什么内容?

    get 2017-01-01
    set 2017-01-01
    monitor { name: 'lx', _r: 123 }
    has true false
    delete { name: 'lx', _r: 123 }
    ownkeys [ 'name', '_r' ]
    

    Reflect

    • Reflect对象与Proxy对象一样,都是ES6对操作对象设计的API
    • 对于我个人的理解而言,Reflect设计的目的是为了优化Object的一些操作方法以及合理的返回Object操作返回的结果,对于一些命令式的Object行为,Reflect对象可以将其变为函数式的行为

    来看个小栗子:

    //旧写法 es5
    try {
      Object.defineProperty(target, property, attributes);
      // success
    } catch (e) {
      // failure
    }
    //新写法 - Reflect对象操作 es6
    if (Reflect.defineProperty(target, property, attributes)) {
      // success
    } else {
      // failure
    }
    

    再看个小栗子:

    let obj = {
         name:"小明",
         age:15
    }
    //旧写法
    console.log(name in obj);// true 
    console.log(Reflect.has(obj,"name"));// true
    

    因为Reflect对象的操作和Proxy对象的操作是一一对应的,所以在Proxy的拦截操作中,可以直接利用Reflect对象直接获取Proxy的默认值。

    再举个小栗子:

    let target = {
         name:"小明",
         age:15
    }
    let handler = {     
      get:function(target,name,property){
          if(name === "name"){
                 console.log("success");
           }else{
                  console.log("success");
           }
            return Reflect.get(target,name,property);
      }
    }
    let pro = new Proxy(target,handler);
    console.log(pro.name)
    //结果为
    // success
    //小明
    

    看了这么多的小栗子,应该明白了Proxy和Reflect之间的关系。

    再看个大栗子:

    function createValidator(target, validator) {
    return new Proxy(target, {
      _validator: validator,
      set(target, key, value, proxy) {
        if (target.hasOwnProperty(key)) {
          let validator = this._validator[key];
          if (!!validator(value)) {
            Reflect.set(target, key, value, proxy)
          } else {
            // console.log(`cannot set ${key} to ${value}`)
            throw Error(`cannot set ${key} to ${value}`)
          }
        } else {
          throw Error(`${key} is not a valid property`)
        }
      }
    })
    }
    
    const personValidator = {
    name(val) { return typeof val === 'string' },
    age(num) { return typeof num === 'number' }
    }
    
    class Person {
    constructor(name, age) {
      this.name = name;
      this.age = age;
      return createValidator(this, personValidator)
    }
    }
    const validator = new Person("zlq", 27)
    validator.name = 0
    validator.age = 'Bill';  
    // validator.age = 0
    // validator.name = 'Bill'; 
    console.log(validator)
    

    这个栗子的输出的结果是什么呢?你猜猜看!

    注意!!!
    如果 Proxy对象和 Reflect对象联合使用,前者拦截赋值操作,后者完成赋值的默认行为,而且传入了receiver,那么Reflect.set会触发Proxy.defineProperty拦截。

    Reflect对象一共有 13 个静态方法。

    Reflect.apply(target, thisArg, args)
    Reflect.construct(target, args)
    Reflect.get(target, name, receiver)
    Reflect.set(target, name, value, receiver)
    Reflect.defineProperty(target, name, desc)
    Reflect.deleteProperty(target, name)
    Reflect.has(target, name)
    Reflect.ownKeys(target)
    Reflect.isExtensible(target)
    Reflect.preventExtensions(target)
    Reflect.getOwnPropertyDescriptor(target, name)
    Reflect.getPrototypeOf(target)
    Reflect.setPrototypeOf(target, prototype)

    上面这些方法的作用,大部分与Object对象的同名方法的作用都是相同的,而且它与Proxy对象的方法是一一对应的,就不做赘述,具体每个方式的使用、参数可以参考阮一峰老师的地址:
    http://es6.ruanyifeng.com/#docs/reflect

    基于Proxy和Reflect的观察者模式实现如下:

    //初始化观察者队列
    const queuedObservers = new Set();
    //将监听函数加入队列
    const observe = fun => queuedObservers.add(fun);
    //初始化Proxy对象,设置拦截操作
    const observable = obj => new Proxy(obj, { set });
    function set(target, key, value, receiver) {
      //内部调用对应的 Reflect 方法
      const result = Reflect.set(target, key, value, receiver);
      //额外执行观察者队列
      queuedObservers.forEach(item => item());
      //  return result;
    }
    const target = {
      name: "小小",
      age: 18
    }
    const per = observable(target);
    function printName() {
      console.log(per.name);
    }
    function printAge() {
      console.log(per.age)
    }
    observe(printName);
    observe(printAge);
    per.name = "小"
    

    输出结果如下

    小
    18
    

    参考博客:https://www.jianshu.com/p/9e07f182859b

    参考博客:https://www.cnblogs.com/lskzj/p/9544958.html

    相关文章

      网友评论

          本文标题:ES6 之 Proxy 和 Reflect

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