162、Proxy 和 Reflect


来源 http://caibaojian.com/es6/proxy.html Proxy 同一个拦截器对象,可以设置拦截多个操作。
1、Proxy var obj = new Proxy({}, { get: function (target, key, receiver) { /* target:目标对象 property:属性名 receiver:操作行为所针对的对象,一般指proxy实例本身 */ if (key === 'prototype') { return Object.prototype; } return 'Hello, ' + key; }, set: function (target, key, value, receiver) { /* target:目标对象 propertyName:属性名 propertyValue:属性值 receiver:Proxy实例本身 */ console.log(`setting ${key}!`); return Reflect.set(target, key, value, receiver); }, apply: function (target, key, receiver) { return receiver[0]; }, construct: function (target, key) { return { value: key[1] }; } }); obj.count; obj.count = 1 下面是 Proxy 支持的拦截操作一览。 对于可以设置、但没有设置拦截的操作,则直接落在目标对象上,按照原先的方式产生结果。 (1)get(target, propKey, receiver) 拦截对象属性的读取,比如proxy.foo和proxy['foo']。 最后一个参数receiver是一个对象,可选,参见下面Reflect.get的部分。 (2)set(target, propKey, value, receiver) 拦截对象属性的设置,比如proxy.foo = v或proxy['foo'] = v,返回一个布尔值。 (3)has(target, propKey) 拦截propKey in proxy的操作,以及对象的hasOwnProperty方法,返回一个布尔值。 (4)deleteProperty(target, propKey) 拦截delete proxy[propKey]的操作,返回一个布尔值。 (5)ownKeys(target) 拦截Object.getOwnPropertyNames(proxy) 、Object.getOwnPropertySymbols(proxy) 、Object.keys(proxy) ,返回一个数组。该方法返回对象所有自身的属性,而Object.keys()仅返回对象可遍历的属性。 (6)getOwnPropertyDescriptor(target, propKey) 拦截Object.getOwnPropertyDescriptor(proxy, propKey) ,返回属性的描述对象。 (7)defineProperty(target, propKey, propDesc) 拦截Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs) ,返回一个布尔值。 (8)preventExtensions(target) 拦截Object.preventExtensions(proxy) ,返回一个布尔值。 (9)getPrototypeOf(target) 拦截Object.getPrototypeOf(proxy) ,返回一个对象。 (10)isExtensible(target) 拦截Object.isExtensible(proxy) ,返回一个布尔值。 (11)setPrototypeOf(target, proto) 拦截Object.setPrototypeOf(proxy, proto) ,返回一个布尔值。 如果目标对象是函数,那么还有两种额外操作可以拦截。 (12)apply(target, object, args) 拦截 Proxy 实例作为函数调用的操作,比如proxy(...args) 、proxy.call(object, ...args) 、proxy.apply(...) 。 (13)construct(target, args) 2、Reflect概述 Reflect对象与Proxy对象一样,也是ES6为了操作对象而提供的新API。Reflect对象的设计目的有这样几个。 (1) 将Object对象的一些明显属于语言内部的方法(比如Object.defineProperty),放到Reflect对象上。现阶段,某些方法同时在Object和Reflect对象上部署,未来的新方法将只部署在Reflect对象上。 (2) 修改某些Object方法的返回结果,让其变得更合理。比如,Object.defineProperty(obj, name, desc)在无法定义属性时,会抛出一个错误,而Reflect.defineProperty(obj, name, desc)则会返回false。 // 老写法 try { Object.defineProperty(target, property, attributes); // success } catch (e) { // failure } // 新写法 if (Reflect.defineProperty(target, property, attributes)) { // success } else { // failure } (3) 让Object操作都变成函数行为。某些Object操作是命令式,比如name in obj和delete obj[name],而Reflect.has(obj, name)和Reflect.deleteProperty(obj, name)让它们变成了函数行为。 // 老写法 'assign' in Object // true // 新写法 Reflect.has(Object, 'assign') // true (4)Reflect对象的方法与Proxy对象的方法一一对应,只要是Proxy对象的方法,就能在Reflect对象上找到对应的方法。这就让Proxy对象可以方便地调用对应的Reflect方法,完成默认行为,作为修改行为的基础。也就是说,不管Proxy怎么修改默认行为,你总可以在Reflect上获取默认行为。 Proxy(target, { set: function (target, name, value, receiver) { var success = Reflect.set(target, name, value, receiver); if (success) { log('property ' + name + ' on ' + target + ' set to ' + value); } return success; } }); 上面代码中,Proxy方法拦截target对象的属性赋值行为。它采用Reflect.set方法将值赋值给对象的属性,然后再部署额外的功能。 下面是另一个例子。 var loggedObj = new Proxy(obj, { get(target, name) { console.log('get', target, name); return Reflect.get(target, name); }, deleteProperty(target, name) { console.log('delete' + name); return Reflect.deleteProperty(target, name); }, has(target, name) { console.log('has' + name); return Reflect.has(target, name); } }); 上面代码中,每一个Proxy对象的拦截操作(get、delete 、has),内部都调用对应的Reflect方法,保证原生行为能够正常执行。添加的工作,就是将每一个操作输出一行日志。 有了Reflect对象以后,很多操作会更易读。 // 老写法 Function.prototype.apply.call(Math.floor, undefined, [1.75]) // 1 // 新写法 Reflect.apply(Math.floor, undefined, [1.75]) // 1 Reflect对象的方法 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对象的方法是一一对应的。下面是对其中几个方法的解释。 (1)Reflect.get(target, name, receiver) 查找并返回target对象的name属性,如果没有该属性,则返回undefined。 如果name属性部署了读取函数,则读取函数的this绑定receiver。 var obj = { get foo() { return this.bar(); }, bar: function () { ... } }; // 下面语句会让 this.bar() // 变成调用 wrapper.bar() Reflect.get(obj, "foo", wrapper); (2)Reflect.set(target, name, value, receiver) 设置target对象的name属性等于value。如果name属性设置了赋值函数,则赋值函数的this绑定receiver。 (3)Reflect.has(obj, name) 等同于name in obj。 (4)Reflect.deleteProperty(obj, name) 等同于delete obj[name]。 (5)Reflect.construct(target, args) 等同于new target(...args) ,这提供了一种不使用new,来调用构造函数的方法。 (6)Reflect.getPrototypeOf(obj) 读取对象的__proto__属性,对应Object.getPrototypeOf(obj) 。 (7)Reflect.setPrototypeOf(obj, newProto) 设置对象的__proto__属性,对应Object.setPrototypeOf(obj, newProto) 。 (8)Reflect.apply(fun, thisArg, args) 等同于Function.prototype.apply.call(fun, thisArg, args) 。一般来说,如果要绑定一个函数的this对象,可以这样写fn.apply(obj, args) ,但是如果函数定义了自己的apply方法,就只能写成Function.prototype.apply.call(fn, obj, args) ,采用Reflect对象可以简化这种操作。 另外,需要注意的是,Reflect.set() 、Reflect.defineProperty() 、Reflect.freeze() 、Reflect.seal()和Reflect.preventExtensions()返回一个布尔值,表示操作是否成功。它们对应的Object方法,失败时都会抛出错误。 // 失败时抛出错误 Object.defineProperty(obj, name, desc); // 失败时返回false Reflect.defineProperty(obj, name, desc); 上面代码中,Reflect.defineProperty方法的作用与Object.defineProperty是一样的,都是为对象定义一个属性。但是,Reflect.defineProperty方法失败时,不会抛出错误,只会返回false。 实例:使用 Proxy 实现观察者模式 观察者模式(Observer mode)指的是函数自动观察数据对象,一旦对象有变化,函数就会自动执行。 const person = observable({ name: '张三', age: 20 }); function print() { console.log(`${person.name}, ${person.age}`) } observe(print); person.name = '李四'; // 输出 // 李四, 20 上面代码中,数据对象person是观察目标,函数print是观察者。一旦数据对象发生变化,print就会自动执行。 下面,使用 Proxy 写一个观察者模式的最简单实现,即实现observable和observe这两个函数。思路是observable函数返回一个原始对象的 Proxy 代理,拦截赋值操作,触发充当观察者的各个函数。 const queuedObservers = new Set(); const observe = fn => queuedObservers.add(fn); const observable = obj => new Proxy(obj, { set }); function set(target, key, value, receiver) { const result = Reflect.set(target, key, value, receiver); queuedObservers.forEach(observer => observer()); return result; } 上面代码中,先定义了一个Set集合,所有观察者函数都放进这个集合。然后,observable函数返回原始对象的代理,拦截赋值操作。拦截函数set之中,会自动执行所有观察者。

猜你喜欢

转载自www.cnblogs.com/gushixianqiancheng/p/11929936.html