“睡服”面试官系列第十七篇之Reflect(建议收藏学习)
目錄
?
1. 概述
2. 靜態(tài)方法
2.1Reflect.get(target, name, receiver)
2.2Reflect.set(target, name, value, receiver)
2.3Reflect.has(obj, name)
2.4Reflect.deleteProperty(obj, name)
2.5Reflect.construct(target, args)
2.6Reflect.getPrototypeOf(obj)
2.7Reflect.setPrototypeOf(obj, newProto)
2.8Reflect.apply(func, thisArg, args)
2.9Reflect.defineProperty(target, propertyKey, attributes)
2.10Reflect.getOwnPropertyDescriptor(target, propertyKey)
2.11Reflect.isExtensible (target)
?2.12Reflect.preventExtensions(target)
2.13Reflect.ownKeys (target)
3. 實(shí)例:使用 Proxy 實(shí)現(xiàn)觀察者模式
1. 概述
Reflect 對(duì)象與 Proxy 對(duì)象一樣,也是 ES6 為了操作對(duì)象而提供的新 API。 Reflect 對(duì)象的設(shè)計(jì)目的有這樣幾個(gè)。
(1) 將 Object 對(duì)象的一些明顯屬于語(yǔ)言內(nèi)部的方法(比如 Object.defineProperty ),放到 Reflect 對(duì)象上。現(xiàn)階段,某些方法同時(shí)在 Object 和
Reflect 對(duì)象上部署,未來(lái)的新方法將只部署在 Reflect 對(duì)象上。也就是說(shuō),從 Reflect 對(duì)象上可以拿到語(yǔ)言內(nèi)部的方法。
(2) 修改某些 Object 方法的返回結(jié)果,讓其變得更合理。比如, Object.defineProperty(obj, name, desc) 在無(wú)法定義屬性時(shí),會(huì)拋出一個(gè)錯(cuò)誤,而
Reflect.defineProperty(obj, name, desc) 則會(huì)返回 false 。
(3) 讓 Object 操作都變成函數(shù)行為。某些 Object 操作是命令式,比如 name in obj 和 delete obj[name] ,而 Reflect.has(obj, name) 和
Reflect.deleteProperty(obj, name) 讓它們變成了函數(shù)行為
(4) Reflect 對(duì)象的方法與 Proxy 對(duì)象的方法一一對(duì)應(yīng),只要是 Proxy 對(duì)象的方法,就能在 Reflect 對(duì)象上找到對(duì)應(yīng)的方法。這就讓 Proxy 對(duì)象可以方便
地調(diào)用對(duì)應(yīng)的 Reflect 方法,完成默認(rèn)行為,作為修改行為的基礎(chǔ)。也就是說(shuō),不管 Proxy 怎么修改默認(rèn)行為,你總可以在 Reflect 上獲取默認(rèn)行為
上面代碼中, Proxy 方法攔截 target 對(duì)象的屬性賦值行為。它采用 Reflect.set 方法將值賦值給對(duì)象的屬性,確保完成原有的行為,然后再部署額外的功
能。
下面是另一個(gè)例子
上面代碼中,每一個(gè) Proxy 對(duì)象的攔截操作( get 、 delete 、 has ),內(nèi)部都調(diào)用對(duì)應(yīng)的 Reflect 方法,保證原生行為能夠正常執(zhí)行。添加的工作,就是
將每一個(gè)操作輸出一行日志。
有了 Reflect 對(duì)象以后,很多操作會(huì)更易讀。
2. 靜態(tài)方法
Reflect 對(duì)象一共有 13 個(gè)靜態(tài)方法。
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 對(duì)象的同名方法的作用都是相同的,而且它與 Proxy 對(duì)象的方法是一一對(duì)應(yīng)的。下面是對(duì)它們的解釋。
2.1Reflect.get(target, name, receiver)
Reflect.get 方法查找并返回 target 對(duì)象的 name 屬性,如果沒(méi)有該屬性,則返回 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 屬性部署了讀取函數(shù)(getter),則讀取函數(shù)的 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如果第一個(gè)參數(shù)不是對(duì)象, Reflect.get 方法會(huì)報(bào)錯(cuò)。
Reflect.get(1, 'foo') // 報(bào)錯(cuò) Reflect.get(false, 'foo') // 報(bào)錯(cuò)2.2Reflect.set(target, name, value, receiver)
Reflect.set 方法設(shè)置 target 對(duì)象的 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 屬性設(shè)置了賦值函數(shù),則賦值函數(shù)的 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 對(duì)象和 Reflect 對(duì)象聯(lián)合使用,前者攔截賦值操作,后者完成賦值的默認(rèn)行為,而且傳入了 receiver ,那么 Reflect.set 會(huì)觸發(fā)
Proxy.defineProperty 攔截。
上面代碼中, Proxy.set 攔截里面使用了 Reflect.set ,而且傳入了 receiver ,導(dǎo)致觸發(fā) Proxy.defineProperty 攔截。這是因?yàn)?Proxy.set 的
receiver 參數(shù)總是指向當(dāng)前的 Proxy 實(shí)例(即上例的 obj ),而 Reflect.set 一旦傳入 receiver ,就會(huì)將屬性賦值到 receiver 上面(即 obj ),導(dǎo)致
觸發(fā) defineProperty 攔截。如果 Reflect.set 沒(méi)有傳入 receiver ,那么就不會(huì)觸發(fā) defineProperty 攔截。
如果第一個(gè)參數(shù)不是對(duì)象, Reflect.set 會(huì)報(bào)錯(cuò)
Reflect.set(1, 'foo', {}) // 報(bào)錯(cuò) Reflect.set(false, 'foo', {}) // 報(bào)錯(cuò)2.3Reflect.has(obj, name)
Reflect.has 方法對(duì)應(yīng) name in obj 里面的 in 運(yùn)算符。
var myObject = { foo: 1, }; // 舊寫法 'foo' in myObject // true 上一章 下一章 2017/11/20 Reflect - ECMAScript 6入門 http://es6.ruanyifeng.com/#docs/reflect 5/9 // 新寫法 Reflect.has(myObject, 'foo') // true如果第一個(gè)參數(shù)不是對(duì)象, Reflect.has 和 in 運(yùn)算符都會(huì)報(bào)錯(cuò)
2.4Reflect.deleteProperty(obj, name)
Reflect.deleteProperty 方法等同于 delete obj[name] ,用于刪除對(duì)象的屬性。
?
該方法返回一個(gè)布爾值。如果刪除成功,或者被刪除的屬性不存在,返回 true ;刪除失敗,被刪除的屬性依然存在,返回 false 。
2.5Reflect.construct(target, args)
Reflect.construct 方法等同于 new target(...args) ,這提供了一種不使用 new ,來(lái)調(diào)用構(gòu)造函數(shù)的方法
function Greeting(name) { this.name = name; } // new 的寫法 const instance = new Greeting('張三'); // Reflect.construct 的寫法 const instance = Reflect.construct(Greeting, ['張三']);2.6Reflect.getPrototypeOf(obj)
Reflect.getPrototypeOf 方法用于讀取對(duì)象的 __proto__ 屬性,對(duì)應(yīng) Object.getPrototypeOf(obj) 。
const myObj = new FancyThing(); // 舊寫法 Object.getPrototypeOf(myObj) === FancyThing.prototype; // 新寫法 Reflect.getPrototypeOf(myObj) === FancyThing.prototype;Reflect.getPrototypeOf 和 Object.getPrototypeOf 的一個(gè)區(qū)別是,如果參數(shù)不是對(duì)象, Object.getPrototypeOf 會(huì)將這個(gè)參數(shù)轉(zhuǎn)為對(duì)象,然后再運(yùn)
行,而 Reflect.getPrototypeOf 會(huì)報(bào)錯(cuò)。
2.7Reflect.setPrototypeOf(obj, newProto)
Reflect.setPrototypeOf 方法用于設(shè)置對(duì)象的 __proto__ 屬性,返回第一個(gè)參數(shù)對(duì)象,對(duì)應(yīng) Object.setPrototypeOf(obj, newProto)
const myObj = new FancyThing(); // 舊寫法 Object.setPrototypeOf(myObj, OtherThing.prototype); // 新寫法 Reflect.setPrototypeOf(myObj, OtherThing.prototype);如果第一個(gè)參數(shù)不是對(duì)象, Object.setPrototypeOf 會(huì)返回第一個(gè)參數(shù)本身,而 Reflect.setPrototypeOf 會(huì)報(bào)錯(cuò)。
Object.setPrototypeOf(1, {}) // 1 Reflect.setPrototypeOf(1, {}) // TypeError: Reflect.setPrototypeOf called on non-object如果第一個(gè)參數(shù)是 undefined 或 null , Object.setPrototypeOf 和 Reflect.setPrototypeOf 都會(huì)報(bào)錯(cuò)。
Object.setPrototypeOf(null, {}) // TypeError: Object.setPrototypeOf called on null or undefined Reflect.setPrototypeOf(null, {}) // TypeError: Reflect.setPrototypeOf called on non-object2.8Reflect.apply(func, thisArg, args)
Reflect.apply 方法等同于 Function.prototype.apply.call(func, thisArg, args) ,用于綁定 this 對(duì)象后執(zhí)行給定函數(shù)。
一般來(lái)說(shuō),如果要綁定一個(gè)函數(shù)的 this 對(duì)象,可以這樣寫 fn.apply(obj, args) ,但是如果函數(shù)定義了自己的 apply 方法,就只能寫成
Function.prototype.apply.call(fn, obj, args) ,采用 Reflect 對(duì)象可以簡(jiǎn)化這種操作。
2.9Reflect.defineProperty(target, propertyKey, attributes)
Reflect.defineProperty 方法基本等同于 Object.defineProperty ,用來(lái)為對(duì)象定義屬性。未來(lái),后者會(huì)被逐漸廢除,請(qǐng)從現(xiàn)在開(kāi)始就使用
Reflect.defineProperty 代替它
如果 Reflect.defineProperty 的第一個(gè)參數(shù)不是對(duì)象,就會(huì)拋出錯(cuò)誤,比如 Reflect.defineProperty(1, 'foo') 。
2.10Reflect.getOwnPropertyDescriptor(target, propertyKey)
Reflect.getOwnPropertyDescriptor 基本等同于 Object.getOwnPropertyDescriptor ,用于得到指定屬性的描述對(duì)象,將來(lái)會(huì)替代掉后者
var myObject = {}; Object.defineProperty(myObject, 'hidden', { value: true, enumerable: false, }); // 舊寫法 var theDescriptor = Object.getOwnPropertyDescriptor(myObject, 'hidden'); // 新寫法 var theDescriptor = Reflect.getOwnPropertyDescriptor(myObject, 'hidden')Reflect.getOwnPropertyDescriptor 和 Object.getOwnPropertyDescriptor 的一個(gè)區(qū)別是,如果第一個(gè)參數(shù)不是對(duì)象,
Object.getOwnPropertyDescriptor(1, 'foo') 不報(bào)錯(cuò),返回 undefined ,而 Reflect.getOwnPropertyDescriptor(1, 'foo') 會(huì)拋出錯(cuò)誤,表示參數(shù)非
法
2.11Reflect.isExtensible (target)
Reflect.isExtensible 方法對(duì)應(yīng) Object.isExtensible ,返回一個(gè)布爾值,表示當(dāng)前對(duì)象是否可擴(kuò)展。
const myObject = {}; // 舊寫法 Object.isExtensible(myObject) // true // 新寫法 Reflect.isExtensible(myObject) // true如果參數(shù)不是對(duì)象, Object.isExtensible 會(huì)返回 false ,因?yàn)榉菍?duì)象本來(lái)就是不可擴(kuò)展的,而 Reflect.isExtensible 會(huì)報(bào)錯(cuò)
Object.isExtensible(1) // false Reflect.isExtensible(1) // 報(bào)錯(cuò)?2.12Reflect.preventExtensions(target)
Reflect.preventExtensions 對(duì)應(yīng) Object.preventExtensions 方法,用于讓一個(gè)對(duì)象變?yōu)椴豢蓴U(kuò)展。它返回一個(gè)布爾值,表示是否操作成功
var myObject = {}; // 舊寫法 Object.preventExtensions(myObject) // Object {} // 新寫法 Reflect.preventExtensions(myObject) // true如果參數(shù)不是對(duì)象, Object.preventExtensions 在 ES5 環(huán)境報(bào)錯(cuò),在 ES6 環(huán)境返回傳入的參數(shù),而 Reflect.preventExtensions 會(huì)報(bào)錯(cuò)。
// ES5 環(huán)境 Object.preventExtensions(1) // 報(bào)錯(cuò) // ES6 環(huán)境 Object.preventExtensions(1) // 1 // 新寫法 Reflect.preventExtensions(1) // 報(bào)錯(cuò)2.13Reflect.ownKeys (target)
Reflect.ownKeys 方法用于返回對(duì)象的所有屬性,基本等同于 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)]3. 實(shí)例:使用 Proxy 實(shí)現(xiàn)觀察者模式
觀察者模式(Observer mode)指的是函數(shù)自動(dòng)觀察數(shù)據(jù)對(duì)象,一旦對(duì)象有變化,函數(shù)就會(huì)自動(dòng)執(zhí)行。
const person = observable({ name: '張三', age: 20 }); function print() { console.log(`${person.name}, ${person.age}`) } observe(print); person.name = '李四'; // 輸出 // 李四, 20上面代碼中,數(shù)據(jù)對(duì)象 person 是觀察目標(biāo),函數(shù) print 是觀察者。一旦數(shù)據(jù)對(duì)象發(fā)生變化, print 就會(huì)自動(dòng)執(zhí)行。
下面,使用 Proxy 寫一個(gè)觀察者模式的最簡(jiǎn)單實(shí)現(xiàn),即實(shí)現(xiàn) observable 和 observe 這兩個(gè)函數(shù)。思路是 observable 函數(shù)返回一個(gè)原始對(duì)象的 Proxy 代
理,攔截賦值操作,觸發(fā)充當(dāng)觀察者的各個(gè)函數(shù)。
上面代碼中,先定義了一個(gè) Set 集合,所有觀察者函數(shù)都放進(jìn)這個(gè)集合。然后, observable 函數(shù)返回原始對(duì)象的代理,攔截賦值操作。攔截函數(shù) set 之
中,會(huì)自動(dòng)執(zhí)行所有觀察者
總結(jié)
本博客源于本人閱讀相關(guān)書籍和視頻總結(jié),創(chuàng)作不易,謝謝點(diǎn)贊支持。學(xué)到就是賺到。我是歌謠,勵(lì)志成為一名優(yōu)秀的技術(shù)革新人員。
歡迎私信交流,一起學(xué)習(xí),一起成長(zhǎng)。
推薦鏈接 其他文件目錄參照
“睡服“面試官系列之各系列目錄匯總(建議學(xué)習(xí)收藏)
總結(jié)
以上是生活随笔為你收集整理的“睡服”面试官系列第十七篇之Reflect(建议收藏学习)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 数字图像处理合集终章——车流量统计(后附
- 下一篇: qt播放器