文章目錄
Reflect
1.0 概述
Reflect
對象與Proxy
對象一樣, 也是ES6爲了操作對象而提供的新 API,Reflect
對象的設計目的有這樣幾個.
- 將
Object
對象的一些明顯屬於語言內部的語法(比如Object.defineProperty
) 放到Reflect
對象上. 也就是說, 可以從Reflect
對象上拿到語言內部的方法.- 修改某些
Object
方法的返回結果, 讓其變得更合理. 比如Object.defineProperty(obj, name, desc)
在無法定義屬性時, 會拋出一個錯誤, 而Reflect.defineProterty(obj,name, desc)則返回
false`// 老寫法 try { Object.defineProperty(target, property, attributes); // success } catch (e) { // failure } // 新寫法 if (Reflect.defineProperty(target, property, attributes)) { // success } else { // failure }
讓
Object
操作都變成函數行爲, 某些Object
操作時命令式, 比如name in obj
和delete obj[name]
, 而reflect.has(obj,name)
和Reflect.deleteProperty(obj,name)
讓它們變成了函數行爲.const obj = { name: 'Gene', } // 老寫法 console.log('name' in obj); // 新寫法 Reflect.has(obj, 'name')
Reflect
對象的方法與Proxy
對象的方法一一對應, 只要是Proxy
對象的方法, 就能在Reflect
對象上找到對應的方法. 這就讓Proxy
對象可以方便的調用對應的Reflect
方法, 完成默認行爲, 作爲修改行爲的基礎, 也就是說, 不管Proxy
怎麼修改默認行爲, 你總是可以在Reflect
上獲取默認行爲.
2.0 靜態方法
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)
2.1 Reflect.get(target,name,receiver)
Reflect.get
方法查找並返回target
對象的name
屬性, 如果沒有該name
屬性, 則返回undefined
const obj = { name: 'Gene', age: 18, get baz() { return this.name + '-' + this.age } } console.log(Reflect.get(obj, 'name')); // Gene console.log(Reflect.get(obj, 'age')); // 18 console.log(Reflect.get(obj, 'baz')); //Gene-18
如果
name
屬性部署了讀取函數(getter), 則讀取函數的this
和會綁定receiver
const obj = { foo: 1, bar: 2, get baz() { return this.foo + this.bar } } const myReceiver = { foo: 10, bar: 10, } console.log(Reflect.get(obj, 'baz', myReceiver)); // 20
如果第一個參數不是對象,
Reflect.get
方法會報錯.Reflect.get(1, 'foo') // 報錯 Reflect.get(false, 'foo') // 報錯
2.2 Reflect.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
攔截。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.set
攔截裏面使用了Reflect.set
,而且傳入了receiver
,導致觸發Proxy.defineProperty
攔截。這是因爲Proxy.set
的receiver
參數總是指向當前的Proxy
實例(即上例的obj
),而Reflect.set
一旦傳入receiver
,就會將屬性賦值到receiver
上面(即obj
),導致觸發defineProperty
攔截。如果Reflect.set
沒有傳入receiver
,那麼就不會觸發defineProperty
攔截。
2.3 Reflect.has(obj,name)
Reflect.has
方法對應name in obj
裏面的in
運算符。const obj = { name: 'Gene', } console.log(Reflect.has(obj, 'name')); // true console.log(Reflect.has(obj, 'age')); // false
如果
Reflect.has()
方法的第一個參數不是對象,會報錯。
2.4 Reflect.deleteProperty(obj,name)
Reflect.deleteProperty
方法等同於delete obj[name]
,用於刪除對象的屬性。const myObj = { foo: 'bar' }; // 舊寫法 delete myObj.foo; // 新寫法 Reflect.deleteProperty(myObj, 'foo');
該方法返回一個布爾值。如果刪除成功,或者被刪除的屬性不存在,返回
true
;刪除失敗,被刪除的屬性依然存在,返回false
。
2.5 Reflect.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.6 Reflect.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.7 Reflect.setPrototypeOf(obj,newProto)
Reflect.setPrototypeOf
方法用於設置目標對象的原型(prototype),對應Object.setPrototypeOf(obj, newProto)
方法。它返回一個布爾值,表示是否設置成功。const myObj = {}; // 舊寫法 Object.setPrototypeOf(myObj, Array.prototype); // 新寫法 Reflect.setPrototypeOf(myObj, Array.prototype); myObj.length // 0
如果無法設置目標對象的原型(比如,目標對象禁止擴展),
Reflect.setPrototypeOf
方法返回false
。Reflect.setPrototypeOf({}, null) // true Reflect.setPrototypeOf(Object.freeze({}), null) // false
總結:
上一章: ES6 Proxy構造函數攔截器, Proxy實例的常用方法
下一章:
交流學習添加微信(備註技術交流學習):
Gene199302
該博客爲學習阮一峯 ES6入門課所做的筆記記錄, 僅用來留作筆記記錄和學習理解