將 Object 對象的一些明顯屬於語言內部的方法(好比 Object.defineProperty ),放到 Reflect 對象上。 app
修改某些 Object 方法的返回結果,讓其變得更合理。好比, Object.defineProperty(obj, name, desc) 在沒法定義屬性時,會拋出一個錯誤,而 Reflect.defineProperty(obj, name, desc) 則會返回 false 。 函數
讓 Object 操做都變成函數行爲。某些 Object 操做是命令式,好比 name in obj 和 delete obj[name] ,而 Reflect.has(obj, name) 和 Reflect.deleteProperty(obj, name) 讓它們變成了函數行爲。 this
spa
// 老寫法 Function.prototype.apply.call(Math.floor, undefined, [8.75]) // 8 // 新寫法 Reflect.apply(Math.floor, undefined, [20.5]) // 20
Reflectprototype
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)
Reflect.get 方法查找並返回 target 對象的 name 屬性,若是沒有該屬性,則返回 undefined 。代理
var obj = { name: 'houfee', age: 24, get func() { return this.name + this.age } } console.log(Reflect.get(obj, 'name')) // houfee console.log(Reflect.get(obj, 'age')) // 24 console.log(Reflect.get(obj, 'func')) // houfee24
若是 name 屬性部署了讀取函數(getter),則讀取函數的 this 綁定 receiver 。code
改變this指向server
var obj = { name: 'houfee', age: 24, get func() { return this.name + this.age } } var obj2 = { name: 'houyue', age: 14, } console.log(Reflect.get(obj, 'name')) // houfee console.log(Reflect.get(obj, 'age')) // 24 console.log(Reflect.get(obj, 'func', obj2)) // houyue14
若是第一個參數不是對象, Reflect.get 方法會報錯。對象
blog
var obj = { name: 'houfee', set func(value) { return this.name = value } } var obj2 = { name: 'houyue' } console.log(Reflect.set(obj, 'name', 'houyue')) // true console.log(Reflect.set(obj, 'func', 'houyue')) // true console.log(obj) // {name: "houyue", age: 24}
若是 name 屬性設置了賦值函數,則賦值函數的 this 綁定 receiver 。
var obj = { name: 'houfee', set func(value) { return this.name = value } } var obj2 = { name: 'zhangsan' } console.log(Reflect.set(obj, 'func', 'houyue', obj2)) // true console.log(obj) // {name: "houfee"} console.log(obj2) // {name: "houyue"}
注意,若是 Proxy 對象和 Reflect 對象聯合使用,前者攔截賦值操做,後者完成賦值的默認行爲,並且傳入了 receiver ,那麼 Reflect.set 會觸發 Proxy.defineProperty 攔截。
Proxy 和 Reflect
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 攔截。
let p = { a: 'a' }; let handler = { set(target, key, value, receiver) { console.log('set'); Reflect.set(target, key, value) }, defineProperty(target, key, attribute) { console.log('defineProperty'); Reflect.defineProperty(target, key, attribute); } }; let obj = new Proxy(p, handler); obj.a = 'A'; // set
若是第一個參數不是對象, Reflect.set 會報錯。
Reflect.has 方法對應 name in obj 裏面的 in 運算符。
var myObject = { foo: 1, }; // 舊寫法 console.log('foo' in myObject); // true // 新寫法 console.log(Reflect.has(myObject, 'foo')); // true
該方法返回一個布爾值。若是刪除成功,或者被刪除的屬性不存在,返回 true ;刪除失敗,被刪除的屬性依然存在,返回 false 。
Reflect.deleteProperty 方法等同於 delete obj[name] ,用於刪除對象的屬性。
const myObj = { foo: 'bar' }; // 舊寫法 delete myObj.foo; // 新寫法 Reflect.deleteProperty(myObj, 'foo');
該方法返回一個布爾值。若是刪除成功,或者被刪除的屬性不存在,返回 true ;刪除失敗,被刪除的屬性依然存在,返回 false 。
Reflect.construct 方法等同於 new target(...args) ,這提供了一種不使用 new ,來調用構造函數的方法。
function Func(name) { this.name = name } // new 的寫法 const instance = new Func('張三') // Reflect.construct 的寫法 const instance = Reflect.construct(Func, ['張三'])
Reflect.getPrototypeOf 方法用於讀取對象的 proto 屬性,對應 Object.getPrototypeOf(obj) 。
function Func(name) { this.name = name } // new 的寫法 const instance = new Func('張三') Object.getPrototypeOf(instance) === Func.prototype // Reflect.construct 的寫法 const instance = Reflect.construct(Func, ['張三']) Reflect.getPrototypeOf(instance) === Func.prototype
Reflect.getPrototypeOf 和 Object.getPrototypeOf 的一個區別是:
若是參數不是對象, Object.getPrototypeOf 會將這個參數轉爲對象,而後再運行,而 Reflect.getPrototypeOf 會報錯。
function Func(name) { this.name = name } function Age(age) { this.age = age } // new 的寫法 將 Age 掛載到 instance 實例的原型上 var instance = new Func('張三') Object.setPrototypeOf(instance, Age.prototype) // Reflect.construct 的寫法 var instance = Reflect.construct(Func, ['張三']) Reflect.setPrototypeOf(instance, Age.prototype)
若是第一個參數不是對象, Object.setPrototypeOf 會返回第一個參數自己,而 Reflect.setPrototypeOf 會報錯。
若是第一個參數是 undefined 或 null , Object.setPrototypeOf 和 Reflect.setPrototypeOf 都會報錯。
const ages = [11, 33, 12, 54, 18, 96]; // 舊寫法 const youngest = Math.min.apply(Math, ages); const oldest = Math.max.apply(Math, ages); const type = Object.prototype.toString.call(youngest); // 新寫法 const youngest = Reflect.apply(Math.min, Math, ages); const oldest = Reflect.apply(Math.max, Math, ages); const type = Reflect.apply(Object.prototype.toString, youngest, []);
function MyDate() { /*…*/ } // 舊寫法 Object.defineProperty(MyDate, 'now', { value: () => Date.now() }); // 新寫法 Reflect.defineProperty(MyDate, 'now', { value: () => Date.now() });
若是 Reflect.defineProperty 的第一個參數不是對象,就會拋出錯誤,好比 Reflect.defineProperty(1, 'foo') 。
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.isExtensible 方法對應 Object.isExtensible ,返回一個布爾值,表示當前對象是否可擴展。
const myObject = {}; // 舊寫法 Object.isExtensible(myObject) // true // 新寫法 Reflect.isExtensible(myObject) // true
若是參數不是對象, Object.isExtensible 會返回 false ,由於非對象原本就是不可擴展的,而 Reflect.isExtensible 會報錯。
Reflect.preventExtensions 對應 Object.preventExtensions 方法,用於讓一個對象變爲不可擴展。它返回一個布爾值,表示是否操做成功。
var myObject = {}; // 舊寫法 Object.preventExtensions(myObject) // Object {} // 新寫法 Reflect.preventExtensions(myObject) // true
若是參數不是對象, Object.preventExtensions 在 ES5 環境報錯,在 ES6 環境返回傳入的參數,而 Reflect.preventExtensions 會報錯。
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)]
const person = observable({ name: '張三', age: 20 }); function print() { console.log(`${person.name}, ${person.age}`) } observe(print); person.name = '李四'; // 輸出 // 李四, 20
上面代碼中,數據對象 person 是觀察目標,函數 print 是觀察者。一旦數據對象發生變化, print 就會自動執行。
const queuedObservers = new Set(); const observe = fn => queuedObservers.add(fn); const observable = obj => new Proxy(obj, { set }); function set(target, key, value, receiver) { const result = Reflect.set(target, key, value, receiver); queuedObservers.forEach(observer => observer()); return result; }
上面代碼中,先定義了一個 Set 集合,全部觀察者函數都放進這個集合。而後, observable 函數返回原始對象的代理,攔截賦值操做。攔截函數 set 之中,會自動執行全部觀察者。