ES6+ Reflect(二)
1. 前言
上一節我們學習了 Reflect 的使用和一些基本的API,本節我們將繼續學習 Reflect 的一些擴展的API。
2 Reflect 擴展方法
2.1 Reflect.defineProperty()
Reflect.defineProperty()
方法會直接在一個對象上定義一個新屬性,或者修改一個對象的現有屬性,基本等同于 Object.defineProperty()
方法,唯一不同是 Object.defineProperty()
返回的是這個對象,Reflect.defineProperty()
返回的是 Boolean
值。
語法:
Reflect.defineProperty(target, propertyKey, attributes)
- target:目標對象;
- propertyKey:需要定義或修改的屬性的名稱;
- attributes:需要定義或修改的屬性的描述。
如果 target 不是 Object
,拋出一個 TypeError
。
let obj = {}
Reflect.defineProperty(obj, 'a', {value: 10}) // true
obj.a; // 10
Reflect.defineProperty
方法可以根據返回值檢查屬性是否被成功定義,而 Object.defineProperty
只能通過 try...catch
去捕獲其中的錯誤,相比之下 Reflect.defineProperty()
方法更加方便。
var obj = {}
var r = Reflect.defineProperty(obj, 'a', {value: 10})
if (r) {
// 成功 todo
} else {
// 失敗 todo
}
try {
let obj = {}
Object.defineProperty(obj, 'a', {value: 10})
} catch(e) {
// 如果失敗,捕獲的異常
}
2.2 Reflect.apply()
**Reflect.apply()
** 通過指定的參數列表發起對目標 (target) 函數的調用。
語法:
Reflect.apply(target, thisArgument, argumentsList)
- target:目標函數。
- thisArgument:target 函數調用時綁定的 this 對象。
- argumentsList:target 函數調用時傳入的實參列表,該參數應該是一個類數組的對象。
apply
函數我們都知道,它可以讓函數執行并可以改變 this
指向。
const arr = [1, 6, 7, 10, 2, 5];
let max;
max = Math.max.apply(null, arr);
console.log(max); // 10
Reflect.apply()
方法與
上面的代碼中 fn.apply(obj, args)
的寫法還可以寫成 Function.prototype.apply.call(func, thisArg, args)
,Function.prototype.apply.call(fn, obj, args)
這和 Reflect.apply()
的調用時傳參是一樣的。都是用于綁定 this
對象然后執行給定函數,Reflect
對象則簡化這種操作。
max = Function.prototype.apply.call(Math.max, null, arr);
console.log(max); // 10
max = Reflect.apply(Math.max, null, arr);
console.log(max); // 10
Reflect.apply()
可以接收截取字符串的函數。
let str = 'imooc ES6 wiki';
let newStr;
newStr = Reflect.apply(String.prototype.slice, str, [6, 9]);
console.log(newStr); // ES6
newStr = str.slice(6, 9);
console.log(newStr); // ES6
newStr = String.prototype.slice.apply(str, [6, 9]);
console.log(newStr); // ES6
2.3 Reflect.construct(target, args)
Reflect.construct()
和 new
操作符構造函數相似 ,相當于運行 new target(...args)
,提供了一種新的不使用 new 來調用構造函數的方法。
語法:
Reflect.construct(target, argumentsList[, newTarget])
參數:
- target:被運行的目標構造函數;
- argumentsList:類數組,目標構造函數調用時的參數;
- newTarget:(可選)作為新創建對象的原型對象的
constructor
屬性,默認值為target
。
下面的兩種實例化的方式是一樣的。
function Foo() {
console.log(arguments);
}
var obj = new Foo(...args);
var obj = Reflect.construct(Foo, args);
Reflect.construct()
返回值是以 target
函數為構造函數,如果 newTarget
存在,則為 newTarget
。argumentList
為其初始化參數。
對于有沒有傳遞第三個參數,我們可以這樣理解:target 就是唯一的構造函數,但是如果傳遞了第三個參數,那就表示:我們的實例由兩部分組成,實例上綁定在 this 上的屬性部分由第一個參數的構造函數生成;不是實例上的屬性部分則由第三個參數的構造函數生成。下面我們來看下具體的實例:
class A {
constructor(name) {
console.log('init A class');
this.name = name || 'Jack';
}
getName() {
console.log(this.name);
return this.name;
}
}
class B {
constructor(age) {
console.log('init A class');
this.age = age || 18;
}
getAge() {
console.log(this.age);
return this.age;
}
}
// 使用A類作為構造函數
let a = Reflect.construct(A, ['David']);
// 使用B類作為構造函數
let b = Reflect.construct(A, ['David'], B);
console.log(a);
console.log(b);
a.getName();
b.getAge();
下圖是上面代碼的打印結果,創建實例 a 時沒有第三個參數,它的原型上的 constructor
指向的是類 A,并且有 getName
方法。創建實例 b 時有第三個參數,打印的結果可以看到實例 b 原型上的 constructor
執行的是類 B,并且有 B 上的 getAge
方法。
3. 小結
本節主要講解了 Reflect 擴展方法的使用