Reflect是ES6爲操做對象而提供的新API,而這個API設計的目的只要有:es6
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對象,不少操做會更易讀app
// 老寫法 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對象的方法一一對應的。this
Reflect.get方法查找並返回target的name屬性,若是沒有,則返回undefined。spa
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。prototype
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。code
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攔截。server
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; }
上面代碼中,先定義一個set集合,全部觀察者函數都放進這個集合。而後,observable函數返回原始對象的代理,攔截賦值操做。攔截函數set中,會自動執行全部觀察者。
原文連接:http://es6.ruanyifeng.com/#docs/reflect