Proxy和Reflect

ES6新增的两个方法,将之前对于对象深层次的操作可以转移在专有方法中处理

Proxy

Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。

用处:

  • 实现拦截和监视外部的访问(set/get)
  • 降低函数和类的复杂度
  • 在复杂操作前做自定义的处理

场景:

  • 抽离校验模块
  • 私有属性
  • 访问日志
  • 预警和拦截
  • 过滤性操作
  • 中间层代理

Proxy接收两个参数,返回一个带有'代理'功能的对象.

  • target:要包装的对象,可以是任何参数,包括函数。
  • handler:代理配置,带有‘钩子’函数的对象,如set\get等方法
// 写一个对象obj,当读取对象中的属性不存在时返回0
var obj = new Proxy({value: '100'}, {
    get: (target, key, receiver) => {
        return key in target ? target[key] : 0;
    }
});

console.log(obj.value);// 100
console.log(obj.xx);// 0

1
2
3
4
5
6
7
8
9
10

可选择除了get还有set、has、ownKeys等,具体可以查看MDN文档open in new window

Proxy和Object.defineProperty的区别:

  • Proxy可以直接代理一个对象,而后者只可以拦截这个对象的属性
  • Proxy可以监听的方法更多
  • Proxy可以对数组进行代理,功能更强大
  • 用法不同,Proxy的方式更直接,更便于理解

Reflect

Reflect将对应已有的方法,从命令式改为函数式,对比一下两者的区别,如:判断一个属性是否在对象中:

let obj={a: 'a'}
// 传统写法
'a' in obj
// Reflect写法
Reflect.has(obj,'a')
1
2
3
4
5

将Reflect当作Object的工具类来用,Proxy和Reflect就成了Object的中间件了。

该Reflect API旨在补充 Proxy。对于任何 Proxy 钩子,都有一个带有相同参数的 Reflect 调用。我们应该使用它们将调用转发给目标对象。

示例:

var obj = new Proxy({}, {
    get: (target, key, receiver) => {
        console.log('这里可以记录访问日志。');
        console.log('如果要设置私有属性,那么这里直接抛出一个错误不让访问。',target, key, receiver);
        if (key !== "value") {
            console.log('这里可以预警和拦截');
        }
        return Reflect.get(target, key, receiver);//这里也可以直接操作target[key]
    },
    // set: ...,//这里可以过滤一些操作。
    // apply: ...,//当把obj当作函数执行,如obj()就会进入apply方法。
    // construct: ...,//当new obj() 的时候就会执行construct方法。
    // ... //还有很多方法,请自行去看文档。
});
obj.value = 2;
console.log(obj.value);//就打印出来2,如果有预警和拦截,那么访问别的属性就访问不了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

ES6规范后,更希望数据和方法进行分离,对象是纯数据,所有的逻辑都放到reflect上,将不会在新增Object的方法

监听Map、Set、Date、Promise等都是用了所谓的'内部插槽'的方式,也就是说访问他们的属性是不可以通过Proxy的set、set方法进行拦截的

let map = new Map();

let proxy = new Proxy(map, {});

proxy.set('test', 1); // Error
1
2
3
4
5

幸运的是可以使用Reflect解决

let map = new Map();

let proxy = new Proxy(map, {
  get(target, prop, receiver) {
    let value = Reflect.get(...arguments);
    return typeof value == 'function' ? value.bind(target) : value;
  }
});

proxy.set('test', 1);
alert(proxy.get('test')); // 1 (works!)
1
2
3
4
5
6
7
8
9
10
11

总结

  • Proxy就是对象的包装了一层代理
  • Reflet就是对象的内部方法的工具类

参考