概述
Reflect
對象是一個全局的普通的對象甩十。Reflect
的原型就是 Object
麻顶。
我們來驗證下 Reflect
的原型是否是 Object
, 代碼如下:
const obj = {}
console.log(Reflect.__proto__ === Object.prototype); // true
console.log(Reflect.__proto__ === obj.__proto__);
Reflect
是 ES6 為了操作對象而新增的 API
,Reflect
對象的設計目的有這樣幾個坟岔。
1) 將 Object
對象的一些明顯屬于語言內(nèi)部的方法(比如 Object.defineProperty
)放到Reflect
對象上。現(xiàn)階段,某些方法同時在Object
和 Reflect
對象上部署来庭,未來的新方法將只部署在 Reflect
對象上。也就是說穿挨,從 Reflect
對象上可以拿到語言內(nèi)部的方法月弛。
2)使用對象 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)有了 Reflect
對象以后,很多操作會更易讀贞绵。
// 老寫法
Function.prototype.apply.call(Math.floor, undefined, [1.22]) // 1
// 新寫法
Reflect.apply(Math.floor, undefined, [1.22]) // 1
靜態(tài)方法
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)
Reflect.get(target, name, receiver)
參數(shù)解析
- target:目標對象。
- name:要讀取的屬性。
- receiver:上下文
this
對象(可選)
Reflect.get
方法查找返回 target
對象的 name
屬性谴垫,如果沒有該屬性章母,則返回 undefined
const person = {
name: "張三",
age: 18,
get habby() {
return this.name + this.age + "歲"
}
};
Reflect.get(person, "name"); // 張三
Reflect.get(person, "age"); // 18
Reflect.get(person, "habby"); // 張三18歲
如果 name
屬性部署了讀取函數(shù) getter
,則讀取函數(shù)的 this
綁定 receiver
翩剪。
const person = {
name: "李四",
age: 20,
get habby() {
return this.name + this.age + "歲";
}
}
const person1 = {
name: "王五",
age: 30
}
Reflect.get(person, "habby", person1); // 王五30歲
如果第一個參數(shù)不是對象乳怎,Reflect.get
方法會報錯
Reflect.get(1, "habby"); // 報錯
Reflect.get(false, "habby"); // 報錯
Reflect.set(target, name, value, receiver)
參數(shù)解析
- target:目標對象。
- name:要讀取的屬性前弯。
- value:要設置的屬性值蚪缀。
- receiver:上下文
this
指向。
Reflect.set
方法設置 target
對象的 name
屬性等于 value
博杖。
const person = {
name: "張三",
set habby(value) {
return this.name = value;
}
};
person.name; // 張三
Reflect.set(person, "name", "李四");
person.name; // 李四
Reflect.set(person, "habby", "王五");
person.habby; // 王五
如果 name
屬性設置了賦值函數(shù)椿胯,則賦值函數(shù)的 this
綁定 receiver
。
const person = {
name: "張三",
set habby(value) {
return this.name = value;
}
};
const person1 = {
name: "李四"
};
Reflect.set(person, "name", "王五", person1);
person.name; // 張三
person1.name; // 王五
注意剃根,如果 Proxy
對象和 Reflect
對象聯(lián)合使用哩盲,前者攔截賦值操作,后者完成賦值的默認行為狈醉,而且傳入了 receiver
廉油,那么 Reflect.set
會觸發(fā) Proxy.defineProperty
攔截。
const perosn = {
name: "張三"
};
const proxy = new Proxy(perosn, {
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);
}
});
person.name = "王五";
// set
// defineProperty
上面代碼中苗傅,Proxy.set
攔截里面使用了 Reflect.set
抒线,而且傳入了 receiver
,導致觸發(fā)Proxy.defineProperty
攔截渣慕。這是因為 Proxy.set
的 receiver
參數(shù)總是指向當前的 Proxy
實例(即上例的obj)嘶炭,而 Reflect.set
一旦傳入 receiver
,就會將屬性賦值到 receiver
上面(即 obj
)逊桦,導致觸發(fā) defineProperty
攔截眨猎。如果 Reflect.set
沒有傳入 receiver
,那么就不會觸發(fā) defineProperty
攔截强经。
const person = {
name: "張三"
};
const proxy = new Proxy(perosn, {
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);
}
});
person.name = "王五";
// set
如果第一個參數(shù)不是對象睡陪,Reflect.set
會報錯。
Reflect.set(1, "name", {}); // 報錯
Reflect.set(false, "name", {}); // 報錯
Reflect.has(obj, name)
參數(shù)解析
- obj:目標對象
- name:對象中的
key
值
Reflect.has
方法對應 name in obj
里面的 in
運算符
const person = {
name: "張三",
age: 20
};
"name" in person; // true
Reflect.has(person, "name") // true
Reflect.deleteProperty(obj, name)
參數(shù)解析:
- obj:目標對象
- name:對象中的
key
值
Reflect.deleteProperty
方法等同于 delete obj[name]
匿情,用于刪除對象的屬性兰迫。
const person = {
name: "張三",
age: 20
};
delete person.age // 舊寫法
Reflect.deleteProperty(person, "age"); // 新寫法
該方法返回一個布爾值。如果刪除成功炬称,或者被刪除的屬性不存在汁果,返回 true
;刪除失敗玲躯,被刪除的屬性依然存在须鼎,返回 false
鲸伴。
如果 Reflect.deleteProperty()
方法的第一個參數(shù)不是對象,會報錯晋控。
Reflect.construct(target, args)
參數(shù)解析
- target:函數(shù)對象
- args:構(gòu)造函數(shù)參數(shù)對象
Reflect.construct
方法等同于 new target(...args)
汞窗,這是一種不使用 new
,來調(diào)用構(gòu)造函數(shù)的方法赡译。
function Person(name) {
this.name = name;
}
const instance = new Person("張三"); // new 的寫法
const instance = Reflect.construct(Person, ["張三"]) // Reflect.construct 的寫法
如果 Reflect.construct()
方法的第一個參數(shù)不是函數(shù)仲吏,會報錯。
Reflect.getPrototypeOf(obj)
參數(shù)解析
- obj:要讀取的對象蝌焚。
Reflect.getPrototypeOf
方法用于讀取對象的 __proto__
屬性裹唆,對應 Object.getPrototypeOf(obj)
。
const person = new Person();
// 舊寫法
Object.getPrototypeOf(person) === person.prototype;
// 新寫法
Reflect.getPrototypeOf(person) === person.prototype;
Reflect.getPrototypeOf
和 Object.getPrototypeOf
的一個區(qū)別是只洒,如果參數(shù)不是對象许帐,Object.getPrototypeOf
會將這個參數(shù)轉(zhuǎn)為對象罪塔,然后再運行排霉,而 Reflect.getPrototypeOf
會報錯。
Object.getPrototypeOf(1) // Number {[[PrimitiveValue]]: 0}
Reflect.getPrototypeOf(1) // 報錯
Reflect.setPrototypeOf(obj, newProto)
參數(shù)解析
- obj:目標對象罩驻。
- newProto:要設置的原型涝开。
Reflect.setPrototypeOf
方法用于設置目標對象的原型(prototype
)循帐,對應 Object.setPrototypeOf(obj, newProto)
方法。它返回一個布爾值舀武,表示是否設置成功拄养。
const person = {};
Object.setPrototypeOf(person, Array.prototype); // 舊寫法
Reflect.setPrototypeOf(person, Array.prototype); // 新寫法
如果無法設置目標對象的原型(比如,目標對象禁止擴展)银舱,Reflect.setPrototypeOf
方法返回 false
瘪匿。
Reflect.setPrototypeOf({}, null); // true
Reflect.setPrototypeOf(Object.freeze({}), null); // false
如果第一個參數(shù)不是對象,Object.setPrototypeOf
會返回第一個參數(shù)本身寻馏,而 Reflect.setPrototypeOf
會報錯棋弥。
Object.setPrototypeOf(1, {}); // 1
Reflect.setPrototypeOf(1, {}); // TypeError: Reflect.setPrototypeOf called on non-object
如果第一個參數(shù)是 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-object
Reflect.apply(func, thisArg, args)
參數(shù)解析
- func:目標函數(shù)
- thisArg:
func
函數(shù)調(diào)用的時候綁定的this
對象嘁锯。 - args:函數(shù)的參數(shù)列表
Reflect.apply
方法等同于 Function.prototype.apply.call(func, thisArg, args)
宪祥,用于綁定 this
對象后執(zhí)行給定函數(shù)聂薪。
一般來說,如果要綁定一個函數(shù)的 this
對象蝗羊,可以這樣寫 fn.apply(obj, args)
藏澳,但是如果函數(shù)定義了自己的 apply
方法,就只能寫成 Function.prototype.apply.call(fn, obj, args)
耀找,采用 Reflect
對象可以簡化這種操作翔悠。
const ages = [1, 2, 3, 4];
// 舊寫法
const min = Math.min.apply(Math, ages);
const max = Math.max.apply(Math, ages);
const type = Object.prototype.toString.call(min);
// 新寫法
const min = Reflect.apply(Math.min, Math, ages);
const max = Reflect.apply(Math.max, Math, ages);
const type = Reflect.apply(Object.prototype.toString, youngest, []);
Reflect.defineProperty(target, propertyKey, attributes)
參數(shù)解析
- target:目標對象业崖。
- propertyKey:目標對象
key
值。 - attributes:目標對象
value
值蓄愁。
Reflect.defineProperty
方法基本等同于 Object.defineProperty
双炕,用來為對象定義屬性。未來撮抓,后者會被逐漸廢除妇斤,請從現(xiàn)在開始就使用 Reflect.defineProperty
代替它。
function MyDate() {}
// 舊寫法
Object.defineProperty(MyDate, "now", {
value: () => Date.now()
});
// 新寫法
Reflect.defineProperty(MyDate, "now", {
value: () => Date.now()
});
如果 Reflect.defineProperty
的第一個參數(shù)不是對象丹拯,就會拋出錯誤站超,比如
Reflect.defineProperty(1, "name")
這個方法可以與 Proxy.defineProperty
配合使用。
const proxy = new Proxy({}, {
defineProperty(target, prop, descriptor) {
console.log(descriptor);
return Reflect.defineProperty(target, prop, descriptor);
}
});
proxy.name = "張三";
// { value: "張三", writable: true, enumerable: true, configurable: true }
proxy.name // "張三"
Reflect.getOwnPropertyDescriptor(target, propertyKey)
參數(shù)解析
- target:目標對象
- propertyKey:目標對象屬性值
Reflect.getOwnPropertyDescriptor
基本等同于 Object.getOwnPropertyDescriptor
乖酬,用于得到指定屬性的描述對象死相,將來會替代掉后者。
const person = {};
Object.defineProperty(person, "name", {
value: true,
enumerable: false
});
// 舊寫法
var theDescriptor = Object.getOwnPropertyDescriptor(person, "name");
// 新寫法
var theDescriptor = Reflect.getOwnPropertyDescriptor(person, "name");
Reflect.getOwnPropertyDescriptor
和 Object.getOwnPropertyDescriptor
的一個區(qū)別是咬像,如果第一個參數(shù)不是對象算撮,Object.getOwnPropertyDescriptor(1, "name")
不報錯,返回 undefined
施掏,而 Reflect.getOwnPropertyDescriptor(1, "name")
會拋出錯誤钮惠,表示參數(shù)非法。
Reflect.isExtensible (target)
參數(shù)解析
- target:目標對象
Reflect.isExtensible
方法對應 Object.isExtensible
七芭,返回一個布爾值素挽,表示當前對象是否可擴展。
const person = {};
// 舊寫法
Object.isExtensible(person) // true
// 新寫法
Reflect.isExtensible(person) // true
如果參數(shù)不是對象狸驳,Object.isExtensible
會返回 false
预明,因為非對象本來就是不可擴展的,而Reflect.isExtensible
會報錯耙箍。
Object.isExtensible(1) // false
Reflect.isExtensible(1) // 報錯
Reflect.preventExtensions(target)
參數(shù)解析
- target:目標函數(shù)撰糠。
Reflect.preventExtensions
對應 Object.preventExtensions
方法,用于讓一個對象變?yōu)椴豢蓴U展辩昆。它返回一個布爾值阅酪,表示是否操作成功。
var person = {};
// 舊寫法
Object.preventExtensions(person ) // Object {}
// 新寫法
Reflect.preventExtensions(myObject) // true
如果參數(shù)不是對象汁针,Object.preventExtensions
在 ES5 環(huán)境報錯术辐,在 ES6 環(huán)境返回傳入的參數(shù),而Reflect.preventExtensions
會報錯施无。
// ES5 環(huán)境
Object.preventExtensions(1) // 報錯
// ES6 環(huán)境
Object.preventExtensions(1) // 1
// 新寫法
Reflect.preventExtensions(1) // 報錯
Reflect.ownKeys (target)
參數(shù)解析
- target:目標對象辉词。
Reflect.ownKeys
方法用于返回對象的所有屬性,基本等同于 Object.getOwnPropertyNames
與 Object.getOwnPropertySymbols
之和猾骡。
const person = {
foo: 1,
bar: 2,
[Symbol.for('baz')]: 3,
[Symbol.for('bing')]: 4,
};
// 舊寫法
Object.getOwnPropertyNames(person)
// ['foo', 'bar']
Object.getOwnPropertySymbols(person)
//[Symbol(baz), Symbol(bing)]
// 新寫法
Reflect.ownKeys(person)
// ['foo', 'bar', Symbol(baz), Symbol(bing)]
如果 Reflect.ownKeys()
方法的第一個參數(shù)不是對象瑞躺,會報錯敷搪。