一赋续、概述
Reflect
對象與Proxy
對象一樣票彪,也是 ES6 為了操作對象而提供的新 API乔妈。Reflect
對象的設(shè)計目的有這樣幾個。Reflect 可以用于獲取目標(biāo)對象的行為酸员,它與 Object 類似,但是更易讀讳嘱,為操作對象提供了一種更優(yōu)雅的方式幔嗦。它的方法與 Proxy 是對應(yīng)的。
(1) 將
Object
對象的一些明顯屬于語言內(nèi)部的方法(比如Object.defineProperty
)沥潭,放到Reflect
對象上邀泉。現(xiàn)階段,某些方法同時在Object
和Reflect
對象上部署钝鸽,未來的新方法將只部署在Reflect
對象上汇恤。也就是說,從Reflect
對象上可以拿到語言內(nèi)部的方法拔恰。(2) 修改某些
Object
方法的返回結(jié)果因谎,讓其變得更合理。比如颜懊,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
操作都變成函數(shù)行為使鹅。某些Object
操作是命令式,比如name in obj
和delete obj[name]
昌抠,而Reflect.has(obj, name)
和Reflect.deleteProperty(obj, name)
讓它們變成了函數(shù)行為患朱。// 老寫法 'assign' in Object // true // 新寫法 Reflect.has(Object, 'assign') // true
(4)
Reflect
對象的方法與Proxy
對象的方法一一對應(yīng),只要是Proxy
對象的方法炊苫,就能在Reflect
對象上找到對應(yīng)的方法裁厅。這就讓Proxy
對象可以方便地調(diào)用對應(yīng)的Reflect
方法,完成默認行為侨艾,作為修改行為的基礎(chǔ)执虹。也就是說,不管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
)钾腺,內(nèi)部都調(diào)用對應(yīng)的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
對象的方法是一一對應(yīng)的邮府。Reflect對象一共有 13 個靜態(tài)方法。
Reflect.get(target, name, receiver)
:查找并返回target對象的name屬性溉奕,如果沒有該屬性褂傀,則返回undefined。Reflect.set(target, name, value, receiver)
:設(shè)置target對象的name屬性等于value加勤。Reflect.has(target, name)
:對應(yīng)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孙蒙,用來為對象定義屬性项棠。未來,后者會被逐漸廢除挎峦,請從現(xiàn)在開始就使用Reflect.defineProperty代替它香追。Reflect.preventExtensions(target)
:對應(yīng)Object.preventExtensions方法,用于讓一個對象變?yōu)椴豢蓴U展坦胶。它返回一個布爾值透典,表示是否操作成功晴楔。Reflect.getPrototypeOf(target)
:用于讀取對象的proto屬性,對應(yīng)Object.getPrototypeOf(obj)峭咒。Reflect.isExtensible(target)
:對應(yīng)Object.isExtensible税弃,返回一個布爾值,表示當(dāng)前對象是否可擴展讹语。Reflect.setPrototypeOf(target,prototype)
:用于設(shè)置目標(biāo)對象的原型(prototype)钙皮,對應(yīng)Object.setPrototypeOf(obj, newProto)
方法蜂科。它返回一個布爾值顽决,表示是否設(shè)置成功。Reflect.apply(target, thisArg, args)
:等同于Function.prototype.apply.call(func, thisArg, args)导匣,用于綁定this對象后執(zhí)行給定函數(shù)才菠。Reflect.construct(target, args)
:等同于newtarget(…args)
,這提供了一種不使用new贡定,來調(diào)用構(gòu)造函數(shù)的方法赋访。
三、實例:使用 Proxy 實現(xiàn)觀察者模式
觀察者模式(Observer mode)指的是函數(shù)自動觀察數(shù)據(jù)對象缓待,一旦對象有變化蚓耽,函數(shù)就會自動執(zhí)行。
const person = observable({ //數(shù)據(jù)對象person name: '張三', age: 20 }); function print() { //觀察者print console.log(`${person.name}, ${person.age}`) } observe(print); person.name = '李四'; // 輸出 // 李四, 20
上面代碼中旋炒,數(shù)據(jù)對象
person
是觀察目標(biāo)步悠,函數(shù)
觀察者模式實現(xiàn):
使用 Proxy 寫一個觀察者模式的最簡單實現(xiàn),即實現(xiàn)
observable
和observe
這兩個函數(shù)铣除。思路是
observable
函數(shù)返回一個原始對象的 Proxy 代理谚咬,攔截賦值操作,觸發(fā)充當(dāng)觀察者的各個函數(shù)尚粘。const queuedObservers = new Set(); //先定義了一個Set集合 const observe = fn => queuedObservers.add(fn); const observable = obj => new Proxy(obj, {set}); //observable函數(shù)返回原始對象的代理 function set(target, key, value, receiver) { const result = Reflect.set(target, key, value, receiver); queuedObservers.forEach(observer => observer()); return result; }
上面代碼中择卦,
- 先定義了一個
Set
集合,所有觀察者函數(shù)都放進這個集合郎嫁。- 然后互捌,
observable
函數(shù)返回原始對象的代理,攔截賦值操作行剂。- 攔截函數(shù)
set
之中秕噪,會自動執(zhí)行所有觀察者。