一、概述
Reflect對象與Proxy對象一樣,也是 ES6 為了操作對象而提供的新 API。Reflect對象的設計目的有這樣幾個。Reflect 可以用于獲取目標對象的行為,它與 Object 類似,但是更易讀,為操作對象提供了一種更優(yōu)雅的方式。它的方法與 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方法,保證原生行為能夠正常執(zhí)行。添加的工作,就是將每一個操作輸出一行日志。
- 有了
Reflect對象以后,很多操作會更易讀。// 老寫法 Function.prototype.apply.call(Math.floor, undefined, [1.75]) // 1 // 新寫法 Reflect.apply(Math.floor, undefined, [1.75]) // 1
二、靜態(tài)方法
- 這些方法的作用,大部分與
Object對象的同名方法的作用都是相同的,而且它與Proxy對象的方法是一一對應的。Reflect對象一共有 13 個靜態(tài)方法。
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方法,用于讓一個對象變?yōu)椴豢蓴U展。它返回一個布爾值,表示是否操作成功。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對象后執(zhí)行給定函數。Reflect.construct(target, args):等同于newtarget(…args),這提供了一種不使用new,來調用構造函數的方法。
三、實例:使用 Proxy 實現觀察者模式
觀察者模式(Observer mode)指的是函數自動觀察數據對象,一旦對象有變化,函數就會自動執(zhí)行。
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 代理,攔截賦值操作,觸發(fā)充當觀察者的各個函數。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之中,會自動執(zhí)行所有觀察者。