從C#到TypeScript - Reflect
在C#里如果想只通過(guò)名字來(lái)生成類實(shí)例、獲取屬性或執(zhí)行方法可以使用反射,反射是基于元數(shù)據(jù),現(xiàn)在很多流行語(yǔ)言都支持元數(shù)據(jù),以此來(lái)提供更多便利的功能。
ES6和TypeScript也有Reflect,不過(guò)因?yàn)镴avaScript本身是解釋型語(yǔ)言,很多操作如根據(jù)名字字符串獲取屬性,根據(jù)字符串執(zhí)行函數(shù)這些原本就有支持,Reflect只是把這些操作歸結(jié)到一起。
下面來(lái)通過(guò)例子來(lái)看下TS Reflect常見(jiàn)的用法。
Reflect Get/Set
定義如下:
Reflect.get(target, name, receiver); Reflect.set(target, name, value, receiver);
看上去也很好理解,和C#很類似:
target:操作的對(duì)象
name:名字字符串
value:要賦的值
receiver:這個(gè)比較怪,因?yàn)轭惱锟梢杂術(shù)etter/setter屬性,這兩種操作可以在代碼塊里使用this
,如果要用Reflect操作的話,receiver就會(huì)代替這個(gè)this
。
Reflect的操作即使是類的private變量也能獲取到。
class Test{ constructor(age: number){ this.age = age; } private _age: number; get age(): number{ return this._age; // this 會(huì)被receiver代替 } set age(value: number) { this._age = value; // this 會(huì)被receiver代替 } }class Receiver{ _age: number = 2; }let t = new Test(1);let r = new Receiver();console.info(Reflect.get(t, "_age")); // 1, 獲取t的_age值console.info(Reflect.get(t, "age")); // 1, 獲取t的age值console.info(Reflect.set(t, "age", 3)); // true, 成功設(shè)置age值為3console.info(Reflect.get(t, "age")); // 3, 再次獲取t的age值console.info(Reflect.get(t, "age", r)); // 2, 表面上是t的age,但實(shí)際上獲取的是r的ageconsole.info(Reflect.set(t, "age", 3, r)); // true, 表面上是設(shè)置t的age, 實(shí)際上是設(shè)置r的age值為3console.info(Reflect.get(r, "_age")); // 3, 直接獲取r的_age
apply
上面是屬性,還有方法,定義如下:
Reflect.apply(func, thisArg, args);
熟悉JS的朋友應(yīng)該知道Function也有apply方法,fn.apply(obj, args)
,可以說(shuō)是同樣的效果。
如果要通過(guò)函數(shù)名來(lái)調(diào)用函數(shù),可以這樣做:
class Test{ add(a: number, b: number): number{ return a + b; } }let t = new Test();console.info(Reflect.apply(t["add"], t, [1, 2])); // 3, 雖然t["add"]可以直接執(zhí)行,不過(guò)有時(shí)可能需要設(shè)置thisArg
define/delete property
define相比之前就真是簡(jiǎn)單把Object替換成了Reflect,delete和delete obj[name]
效果一樣。
Reflect.defineProperty(target, propertyKey, attributes); Reflect.deleteProperty(obj, name);
例子延用上面的對(duì)象t:
//defineReflect.defineProperty(t, 'time', { value: Date.now() });console.info(t.time); // 一串?dāng)?shù)字//deletelet d = {time: 111};console.info(d.time); // 111Reflect.deleteProperty(d, 'time'); // 成功的話返回true,否則返回falseconsole.info(d.time); // undefined
可以看到define的參數(shù)attributes
是一個(gè)PropertyDescriptor對(duì)象,value
就是值,其他還有writable, enumerable, configurable
用來(lái)控制屬性的權(quán)限。
對(duì)于delete,需要注意的是deleteProperty對(duì)class的屬性是無(wú)效的。
has ownKeys
ownKeys
返回的是對(duì)象所有屬性,包括不可枚舉的,如Symbol之類。has
用來(lái)判斷對(duì)象是否有某個(gè)屬性或方法,包括原型鏈上的。
class Test{ constructor(name: string){ this.name = name; } name: string; flag: Symbol = Symbol('flag'); getName(): string{ return this.name; } }let obj = new Test('123');console.info(Reflect.has(obj, 'name')); // trueconsole.info(Reflect.has(obj, 'flag')); // trueconsole.info(Reflect.has(obj, 'get')); // trueconsole.info(Reflect.has(obj, 'toString')); // truefor(let p of Reflect.ownKeys(obj)){ console.info(p); // name, flag}
其他
Reflect.construct(target,args)
實(shí)例化對(duì)象除了new之外,還可以用這個(gè),有時(shí)候很有用,比如ORM框架里join的字段就可以在設(shè)置表時(shí)把關(guān)聯(lián)的類型傳給字段,使用時(shí)用該類型就可以創(chuàng)建出實(shí)例。
Reflect.getPrototypeOf(target) 和 Reflect.setPrototypeOf(target, prototype)
分別用于獲取和設(shè)置對(duì)象的原型
Reflect.getOwnPropertyDescriptor(target, name)
設(shè)置對(duì)象屬性的描述對(duì)象,如
configurable, writable, enumerable
。Reflect.isExtensible(target)
分別用于判斷對(duì)象是否可擴(kuò)展。
Reflect.preventExtensions(target)
讓一個(gè)對(duì)象變?yōu)椴豢蓴U(kuò)展
Reflect基本上就是把之前Object的方法和一些命令如delete
in
之類聚到一起,相信ES6之后用Reflect來(lái)做這些操作將成為主流。