Reflect是ES6为操作对象而提供的新API,而这个API设计的目的只要有:
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属性,如果没有,则返回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。
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对应 name in obj 里面的in操作
var myObject = { foo: 1, }; // 旧写法 'foo' in myObject // true // 新写法 Reflect.has(myObject, 'foo') // true
如果第一个参数不是对象,Reflect.has和in都会报错。
Reflect.deleteProperty方法等同于delete obj[name],用于删除对象属性。
const myObj = { foo: 'bar' }; // 旧写法 delete myObj.foo; // 新写法 Reflect.deleteProperty(myObj, 'foo');
该方法返回一个布尔值。如果删除成功或删除的属性不存在,则返回true,如果删除失败,删除的属性依然还在,则返回false。
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方法用读取对象的__proto__属性,对应Object.getPrototypeOf(obj)方法。
const myObj = new FancyThing(); // 旧写法 Object.getPrototypeOf(myObj) === FancyThing.prototype; // 新写法 Reflect.getPrototypeOf(myObj) === FancyThing.prototype;
它们的区别是,如果参数不是对象,Object.getPrototypeOf会将参数转化为对象,而Reflect.getPrototypeOf会报错。
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等同于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等同于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方法等同于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等同于Object.isExtensible,返回一个布尔值,表示当前对象是否可扩展。
const myObject = {}; // 旧写法 Object.isExtensible(myObject) // true // 新写法 Reflect.isExtensible(myObject) // true
如果参数不是对象,Object.isExtensible会返回false,因为本来就是不可扩展的,而Reflect.isExtensible则报错。
Reflect.preventExtensions等同于Object.preventExtensions,用于让一个对象变为不可扩展,返回一个布尔值,表示是否操作成功。
var myObject = {}; // 旧写法 Object.preventExtensions(myObject) // Object {} // 新写法 Reflect.preventExtensions(myObject) // true
如果参数不是对象,Object.preventExtensions在ES5下会报错,ES6下会返回传入的值,Reflect.preventExtensions则会报错。
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; }https://www.imdb.com/list/ls080845509/?ref_=baidu_uzim https://www.imdb.com/list/ls080845571/?ref_=baidu_4mi6 https://www.imdb.com/list/ls080845579/?ref_=baidu_ad9l https://www.imdb.com/list/ls080845535/?ref_=baidu_4745 https://www.imdb.com/list/ls080845567/?ref_=baidu_pzvf https://www.imdb.com/list/ls080845528/?ref_=baidu_gizd https://www.imdb.com/list/ls080845541/?ref_=baidu_m19z https://www.imdb.com/list/ls080845580/?ref_=baidu_jb9q https://www.imdb.com/list/ls080845581/?ref_=baidu_m7jd https://www.imdb.com/list/ls080845709/?ref_=baidu_sbq3 https://www.imdb.com/list/ls080845752/?ref_=baidu_hk2a https://www.imdb.com/list/ls080845778/?ref_=baidu_yin1 https://www.imdb.com/list/ls080845711/?ref_=baidu_thjc https://www.imdb.com/list/ls080845739/?ref_=baidu_k21l https://www.imdb.com/list/ls080845763/?ref_=baidu_2jbr https://www.imdb.com/list/ls080845728/?ref_=baidu_z3q2 https://www.imdb.com/list/ls080845743/?ref_=baidu_84q9 https://www.imdb.com/list/ls080845794/?ref_=baidu_5qk1 https://www.imdb.com/list/ls080845782/?ref_=baidu_2ji5 https://www.imdb.com/list/ls080845109/?ref_=baidu_84za https://www.imdb.com/list/ls080845153/?ref_=baidu_t1iz https://www.imdb.com/list/ls080845115/?ref_=baidu_xvfs https://www.imdb.com/list/ls080845112/?ref_=baidu_k9e5 https://www.imdb.com/list/ls080845160/?ref_=baidu_amn9 https://www.imdb.com/list/ls080845121/?ref_=baidu_9unj https://www.imdb.com/list/ls080845141/?ref_=baidu_lq8p https://www.imdb.com/list/ls080845191/?ref_=baidu_ar8b https://www.imdb.com/list/ls080845182/?ref_=baidu_eoeu https://www.imdb.com/list/ls080845309/?ref_=baidu_sl9j https://www.imdb.com/list/ls080845352/?ref_=baidu_yyhy https://www.imdb.com/list/ls080845374/?ref_=baidu_hqto https://www.imdb.com/list/ls080845333/?ref_=baidu_h1qj https://www.imdb.com/list/ls080845361/?ref_=baidu_0qv3 https://www.imdb.com/list/ls080845322/?ref_=baidu_fde4 https://www.imdb.com/list/ls080845344/?ref_=baidu_ocqg https://www.imdb.com/list/ls080845399/?ref_=baidu_2om7 https://www.imdb.com/list/ls080845382/?ref_=baidu_y071 https://www.imdb.com/list/ls080845653/?ref_=baidu_rvds https://www.imdb.com/list/ls080845673/?ref_=baidu_gko1 https://www.imdb.com/list/ls080845631/?ref_=baidu_a08z https://www.imdb.com/list/ls080845665/?ref_=baidu_bjk0 https://www.imdb.com/list/ls080845625/?ref_=baidu_8mlk https://www.imdb.com/list/ls080845645/?ref_=baidu_59xh https://www.imdb.com/list/ls080845690/?ref_=baidu_189q https://www.imdb.com/list/ls080845699/?ref_=baidu_65wz https://www.imdb.com/list/ls080845206/?ref_=baidu_9umr https://www.imdb.com/list/ls080845255/?ref_=baidu_zji7 https://www.imdb.com/list/ls0808