Reflect是ES6为操作对象而提供的新API,而这个API设计的目的只要有:
- 将Object对象的一些属于语言内部的方法放到Reflect对象上,从Reflect上能拿到语言内部的方法。如:Object.defineProperty
- 修改某些object方法返回的结果。如:Object.defineProperty(obj, name, desc)在无法定义属性的时候会报错,而Reflect.defineProperty(obj, name, desc)则会返回false
- 让Object的操作都变成函数行为。如object的命令式:name in obj和delete obj[name] 则与 Reflect.has(obj, name)、Reflect.deleteProperty(obj, name)相等
- Reflect对象的方法与Proxy对象的方法一一对应,只要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; } });
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); } });
有了Reflect对象,很多操作会更易读
// 老写法 Function.prototype.apply.call(Math.floor, undefined, [1.75]) // 1 // 新写法 Reflect.apply(Math.floor, undefined, [1.75]) // 1
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对象的方法一一对应的。
Reflect.get(target, name, receiver)
Reflect.get方法查找并返回target的name属性,如果没有,则返回undefined。
var myObject = { foo: 1, bar: 2, get baz() { return this.foo + this.bar; }, } Reflect.get(myObject, 'foo') // 1 Reflect.get(myObject, 'bar') // 2 Reflect.get(myObject, 'baz') // 3
如果name属性部署了读取函数(getter),则读取函数的this绑定的receiver。
var myObject = { foo: 1, bar: 2, get baz() { return this.foo + this.bar; }, }; var myReceiverObject = { foo: 4, bar: 4, }; Reflect.get(myObject, 'baz', myReceiverObject) // 8
如果第一个参数不是对象,则Reflect.get则会报错。
Reflect.set(target, name, value, receiver)
Reflect.set方法设置target对象的name属性等于value。
var myObject = { foo: 1, set bar(value) { return this.foo = value; }, } myObject.foo // 1 Reflect.set(myObject, 'foo', 2); myObject.foo // 2 Reflect.set(myObject, 'bar', 3) myObject.foo // 3
如果name属性设置的赋值函数,则赋值函数的this绑定receiver。
var myObject = { foo: 4, set bar(value) { return this.foo = value; }, }; var myReceiverObject = { foo: 0, }; Reflect.set(myObject, 'bar', 1, myReceiverObject); myObject.foo // 4 myReceiverObject.foo // 1
如果Proxy与Reflect联合使用,前者完成拦截赋值操作,后者完成赋值默认行为,而且传入了receiver,则Reflect.set会触发Proxy.defineProperty拦截。
let p = { a: 'a' }; let handler = { set(target, key, value, receiver) { console.log('set'); Reflect.set(target, key, value, receiver) }, defineProperty(target, key, attribute) { console.log('defineProperty'); Reflect.defineProperty(target, key, attribute); } }; let obj = new Proxy(p, handler); obj.a = 'A'; // set // defineProperty
如果不传,则Proxy不会触发defineProperty拦截。
let p = { a: 'a' }; let handler = { set(target, key, value, receiver) { console.log('set'); Reflect.set(target, key, value) }, defineProperty(target, key, attribute) { console.log('defineProperty'); Reflect.defineProperty(target, key, attribute); } }; let obj = new Proxy(p, handler); obj.a = 'A'; // set
如果第一个参数不是对象,则Reflect.set会报错。
Reflect.has(obj, name)
Reflect.has对应 name in obj 里面的in操作
var myObject = { foo: 1, }; // 旧写法 'foo' in myObject // true // 新写法 Reflect.has(myObject, 'foo') // true
如果第一个参数不是对象,Reflect.has和in都会报错。
Reflect.deleteProperty(obj, name)
Reflect.deleteProperty方法等同于delete obj[name],用于删除对象属性。
const myObj = { foo: 'bar' }; // 旧写法 delete myObj.foo; // 新写法 Reflect.deleteProperty(myObj, 'foo');
该方法返回一个布尔值。如果删除成功或删除的属性不存在,则返回true,如果删除失败,删除的属性依然还在,则返回false。
Reflect.construct(target, args)
Reflect.construct方法等同于new target(...args),这提供了一种不使用new,来调用构造函数的方法。
function Greeting(name) { this.name = name; } // new 的写法 const instance = new Greeting('张三'); // Reflect.construct 的写法 const instance = Reflect.construct(Greeting, ['张三']);
Reflect.getPrototypeOf(obj)
Reflect.getPrototypeOf方法用读取对象的__proto__属性,对应Object.getPrototypeOf(obj)方法。
const myObj = new FancyThing(); // 旧写法 Object.getPrototypeOf(myObj) === FancyThing.prototype; // 新写法 Reflect.getPrototypeOf(myObj) === FancyThing.prototype;
它们的区别是,如果参数不是对象,Object.getPrototypeOf会将参数转化为对象,而Reflect.getPrototypeOf会报错。
Reflect.setPrototypeOf(obj, newProto)
Reflect.setPrototypeOf方法是设置对象的__proto__属性,返回第一个参数对象,对应Object.setPrototypeOf(obj, newProto
const myObj = new FancyThing(); // 旧写法 Object.setPrototypeOf(myObj, OtherThing.prototype); // 新写法 Reflect.setPrototypeOf(myObj, OtherThing.prototype);
如果第一个参数不是对象,Object.setPrototypeOf会返回第一个参数对象,而Reflect.setPrototypeOf会报错。
如果第一个参数是undefined或null,则两个都会报错。
Reflect.apply(func, thisArgs, args)
Reflect.apply等同于Function.prototype.apply.call(func, thisArgs, args),用于绑定this对象后执行给定函数。
一般来说,如果要绑定一个函数的this对象,可以写成这样fn.apply(obj, args),但是如果函数定义了自己的apply方法就只能写成Function.prototype.apply.call(fn, obj, args),采用Reflect简化这种操作
const ages = [11, 33, 12, 54, 18, 96]; // 旧写法 const youngest = Math.min.apply(Math, ages); const oldest = Math.max.apply(Math, ages); const type = Object.prototype.toString.call(youngest); // 新写法 const youngest = Reflect.apply(Math.min, Math, ages); const oldest = Reflect.apply(Math.max, Math, ages); const type = Reflect.apply(Object.prototype.toString, youngest, []);
Reflect.defineProperty(target, propertyKey, attributes)
Reflect.defineProperty等同于Object.defineProperty,用来为对象定义属性。未来后者会被逐渐废除。
function MyDate() { /*…*/ } // 旧写法 Object.defineProperty(MyDate, 'now', { value: () => Date.now() }); // 新写法 Reflect.defineProperty(MyDate, 'now', { value: () => Date.now() });
如果第一个参数不是对象,就会抛出错误信息。
该方法配合Proxy.defineProperty使用:
const p = new Proxy({}, { defineProperty(target, prop, descriptor) { console.log(descriptor); return Reflect.defineProperty(target, prop, descriptor); } }); p.foo = 'bar'; // {value: "bar", writable: true, enumerable: true, configurable: true} p.foo // "bar"
上面代码中,Proxy.defineProperty对属性赋值设置了拦截,Reflect.defineProperty完成了赋值。
Reflect.getOwnPropertyDescriptor(target, propertyKey)
Reflect.getOwnPropertyDescriptor方法等同于Object.getOwnPropertyDescriptor,用于得到指定属性的描述对象,将来会代替后者。
var myObject = {}; Object.defineProperty(myObject, 'hidden', { value: true, enumerable: false, }); // 旧写法 var theDescriptor = Object.getOwnPropertyDescriptor(myObject, 'hidden'); // 新写法 var theDescriptor = Reflect.getOwnPropertyDescriptor(myObject, 'hidden');
如果第一个参数不是对象Object.getOwnPropertyDescriptor不报错,返回undefined,而Reflect.getOwnPropertyDescriptor会报错,表示参数非法。
Reflect.isExtensible (target)
Reflect.isExtensible等同于Object.isExtensible,返回一个布尔值,表示当前对象是否可扩展。
const myObject = {}; // 旧写法 Object.isExtensible(myObject) // true // 新写法 Reflect.isExtensible(myObject) // true
如果参数不是对象,Object.isExtensible会返回false,因为本来就是不可扩展的,而Reflect.isExtensible则报错。
Reflect.preventExtensions(target)
Reflect.preventExtensions等同于Object.preventExtensions,用于让一个对象变为不可扩展,返回一个布尔值,表示是否操作成功。
var myObject = {}; // 旧写法 Object.preventExtensions(myObject) // Object {} // 新写法 Reflect.preventExtensions(myObject) // true
如果参数不是对象,Object.preventExtensions在ES5下会报错,ES6下会返回传入的值,Reflect.preventExtensions则会报错。
Reflect.ownKeys (target)
Reflect.ownKeys方法用于返回对象的所有属性,等同于Object.getOwnPropertyNames与Object.getOwnPropertySymbols之和。
var myObject = { foo: 1, bar: 2, [Symbol.for('baz')]: 3, [Symbol.for('bing')]: 4, }; // 旧写法 Object.getOwnPropertyNames(myObject) // ['foo', 'bar'] Object.getOwnPropertySymbols(myObject) //[Symbol(baz), Symbol(bing)] // 新写法 Reflect.ownKeys(myObject) // ['foo', 'bar', Symbol(baz), Symbol(bing)]
实例:使用Proxy实现观察者模式
观察者模式(Obsever mode)指的是函数自动观察数据变化,一旦对象有变化,函数会自动执行。
const person = observable({ name: '张三', age: 20 }); function print() { console.log(`${person.name}, ${person.age}`) } observe(print); person.name = '李四'; // 输出 // 李四, 20
上面代码中,数据对象person是观察目标,函数print是观察者,一旦person变化,print则自动执行。
下面使用Proxy写一个最简单的观察者模式,即实现observable和observe这两个函数。思路observable函数返回一个原始对象Proxy代理,拦截赋值操作,c触发充当观察者的各个函数。
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中,会自动执行所有观察者。
每一次的记录,都是向前迈进的一步