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