Reflect對象的設(shè)計目的有這樣幾個。
1) 將Object對象的一些明顯屬于語言內(nèi)部的方法(比如Object.defineProperty),放到Reflect對象上?,F(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上獲取默認行為。
Proxy(target, {
set: function(target, name, value, receiver) {
var success = Reflect.set(target,name, value, receiver);
if (success) {
log('property ' + name + ' on ' + target + ' set to ' + value);
}
return success;
}
});
Reflect對象一共有 13 個靜態(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)
什么是get 和set?
Reflect.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屬性部署了讀取函數(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
receiver注意這個參數(shù),注意this在有無receiver參數(shù)的異同,演示
如果第一個參數(shù)不是對象,Reflect.get方法會報錯。
Reflect.get(1, 'foo') // 報錯
Reflect.get(false, 'foo') // 報錯
Reflect.set(target, name, value, receiver)
Reflect.set方法設(shè)置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屬性設(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 對象和 Reflect 對象聯(lián)合使用,前者攔截賦值操作,后者完成賦值的默認行為,而且傳入了receiver,那么Reflect.set會觸發(fā)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
receiver注意這個參數(shù),注意this在有無receiver參數(shù)的異同,演示
實例:使用 Proxy 實現(xiàn)觀察者模式
觀察者模式(Observer mode)指的是函數(shù)自動觀察數(shù)據(jù)對象,一旦對象有變化,函數(shù)就會自動執(zhí)行。
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;
}
const person = observable({
name: '張三',
age: 20
});
function print() {
console.log(`${person.name}, ${person.age}`)
}
function print2() {
console.log('就是任性')
}
observe(print);
observe(print2);
person.name = '李四';
上面代碼中,數(shù)據(jù)對象person是觀察目標,函數(shù)print是觀察者。一旦數(shù)據(jù)對象發(fā)生變化,print就會自動執(zhí)行。
使用 Proxy 寫一個觀察者模式的最簡單實現(xiàn),即實現(xiàn)observable和observe這兩個函數(shù)。思路是observable函數(shù)返回一個原始對象的 Proxy 代理,攔截賦值操作,觸發(fā)充當觀察者的各個函數(shù)。
上面代碼中,先定義了一個Set集合,所有觀察者函數(shù)都放進這個集合。然后,observable函數(shù)返回原始對象的代理,攔截賦值操作。攔截函數(shù)set之中,會自動執(zhí)行所有觀察者。