一、概述
-
Reflect
对象与Proxy
对象一样,也是 ES6 为了操作对象而提供的新 API。Reflect
对象的设计目的有这样几个。 -
Reflect 可以用于获取目标对象的行为,它与 Object 类似,但是更易读,为操作对象提供了一种更优雅的方式。它的方法与 Proxy 是对应的。
(1) 将
Object
对象的一些明显属于语言内部的方法(比如Object.defineProperty
),放到Reflect
对象上。现阶段,某些方法同时在Object
和Reflect
对象上部署,未来的新方法将只部署在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
上获取默认行为。例子1:
Proxy(target, { set: function(target, name, value, receiver) { var success = Reflect.set(target, name, value, receiver); if (success) { console.log('property ' + name + ' on ' + target + ' set to ' + value); } return success; } });
上面代码中,
Proxy
方法拦截target
对象的属性赋值行为。它采用Reflect.set
方法将值赋值给对象的属性,确保完成原有的行为,然后再部署额外的功能。例子2:
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
二、静态方法
- 这些方法的作用,大部分与
Object
对象的同名方法的作用都是相同的,而且它与Proxy
对象的方法是一一对应的。Reflect对象一共有 13 个静态方法。
Reflect.get(target, name, receiver)
:查找并返回target对象的name属性,如果没有该属性,则返回undefined。Reflect.set(target, name, value, receiver)
:设置target对象的name属性等于value。Reflect.has(target, name)
:对应name in obj里面的in运算符。Reflect.deleteProperty(target, name)
:等同于delete obj[name],用于删除对象的属性。Reflect.ownKeys(target)
:用于返回对象的所有属性,基本等同于Object.getOwnPropertyNames与Object.getOwnPropertySymbols之和。Reflect.getOwnPropertyDescriptor(target,name)
:基本等同于Object.getOwnPropertyDescriptor,用于得到指定属性的描述对象,将来会替代掉后者。Reflect.defineProperty(target, name,desc)
:基本等同于Object.defineProperty,用来为对象定义属性。未来,后者会被逐渐废除,请从现在开始就使用Reflect.defineProperty代替它。Reflect.preventExtensions(target)
:对应Object.preventExtensions方法,用于让一个对象变为不可扩展。它返回一个布尔值,表示是否操作成功。Reflect.getPrototypeOf(target)
:用于读取对象的proto属性,对应Object.getPrototypeOf(obj)。Reflect.isExtensible(target)
:对应Object.isExtensible,返回一个布尔值,表示当前对象是否可扩展。Reflect.setPrototypeOf(target,prototype)
:用于设置目标对象的原型(prototype),对应Object.setPrototypeOf(obj, newProto)
方法。它返回一个布尔值,表示是否设置成功。Reflect.apply(target, thisArg, args)
:等同于Function.prototype.apply.call(func, thisArg, args),用于绑定this对象后执行给定函数。Reflect.construct(target, args)
:等同于newtarget(…args)
,这提供了一种不使用new,来调用构造函数的方法。
三、实例:使用 Proxy 实现观察者模式
观察者模式(Observer mode)指的是函数自动观察数据对象,一旦对象有变化,函数就会自动执行。
const person = observable({ //数据对象person name: '张三', age: 20 }); function print() { //观察者print console.log(`${person.name}, ${person.age}`) } observe(print); person.name = '李四'; // 输出 // 李四, 20
上面代码中,数据对象
person
是观察目标,函数
观察者模式实现:
使用 Proxy 写一个观察者模式的最简单实现,即实现
observable
和observe
这两个函数。思路是
observable
函数返回一个原始对象的 Proxy 代理,拦截赋值操作,触发充当观察者的各个函数。const queuedObservers = new Set(); //先定义了一个Set集合 const observe = fn => queuedObservers.add(fn); const observable = obj => new Proxy(obj, {set}); //observable函数返回原始对象的代理 function set(target, key, value, receiver) { const result = Reflect.set(target, key, value, receiver); queuedObservers.forEach(observer => observer()); return result; }
上面代码中,
- 先定义了一个
Set
集合,所有观察者函数都放进这个集合。- 然后,
observable
函数返回原始对象的代理,拦截赋值操作。- 拦截函数
set
之中,会自动执行所有观察者。
网友评论