面試官:說(shuō)說(shuō)你對(duì) TypeScript 中裝飾器的理解?應(yīng)用場(chǎng)景?
一、是什么
裝飾器是一種特殊類(lèi)型的聲明,它能夠被附加到類(lèi)聲明,方法, 訪問(wèn)符,屬性或參數(shù)上
是一種在不改變?cè)?lèi)和使用繼承的情況下,動(dòng)態(tài)地?cái)U(kuò)展對(duì)象功能
同樣的,本質(zhì)也不是什么高大上的結(jié)構(gòu),就是一個(gè)普通的函數(shù),@expression 的形式其實(shí)是Object.defineProperty的語(yǔ)法糖
expression求值后必須也是一個(gè)函數(shù),它會(huì)在運(yùn)行時(shí)被調(diào)用,被裝飾的聲明信息做為參數(shù)傳入
二、使用方式
由于typescript是一個(gè)實(shí)驗(yàn)性特性,若要使用,需要在tsconfig.json文件啟動(dòng),如下:
- {
- "compilerOptions": {
- "target": "ES5",
- "experimentalDecorators": true
- }
- }
typescript裝飾器的使用和javascript基本一致
類(lèi)的裝飾器可以裝飾:
- 類(lèi)
- 方法/屬性
- 參數(shù)
- 訪問(wèn)器
- 類(lèi)裝飾
例如聲明一個(gè)函數(shù) addAge 去給 Class 的屬性 age 添加年齡.
- function addAge(constructor: Function) {
- constructor.prototype.age = 18;
- }
- @addAge
- class Person{
- name: string;
- age!: number;
- constructor() {
- this.name = 'huihui';
- }
- }
- let person = new Person();
- console.log(person.age); // 18
上述代碼,實(shí)際等同于以下形式:
- Person = addAge(function Person() { ... });
上述可以看到,當(dāng)裝飾器作為修飾類(lèi)的時(shí)候,會(huì)把構(gòu)造器傳遞進(jìn)去。constructor.prototype.age 就是在每一個(gè)實(shí)例化對(duì)象上面添加一個(gè) age 屬性
方法/屬性裝飾
同樣,裝飾器可以用于修飾類(lèi)的方法,這時(shí)候裝飾器函數(shù)接收的參數(shù)變成了:
- target:對(duì)象的原型
- propertyKey:方法的名稱(chēng)
- descriptor:方法的屬性描述符
可以看到,這三個(gè)屬性實(shí)際就是Object.defineProperty的三個(gè)參數(shù),如果是類(lèi)的屬性,則沒(méi)有傳遞第三個(gè)參數(shù)
如下例子:
- // 聲明裝飾器修飾方法/屬性
- function method(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
- console.log(target);
- console.log("prop " + propertyKey);
- console.log("desc " + JSON.stringify(descriptor) + "\n\n");
- descriptor.writable = false;
- };
- function property(target: any, propertyKey: string) {
- console.log("target", target)
- console.log("propertyKey", propertyKey)
- }
- class Person{
- @property
- name: string;
- constructor() {
- this.name = 'huihui';
- }
- @method
- say(){
- return 'instance method';
- }
- @method
- static run(){
- return 'static method';
- }
- }
- const xmz = new Person();
- // 修改實(shí)例方法say
- xmz.say = function() {
- return 'edit'
- }
輸出如下圖所示:
參數(shù)裝飾
接收3個(gè)參數(shù),分別是:
- target :當(dāng)前對(duì)象的原型
- propertyKey :參數(shù)的名稱(chēng)
- index:參數(shù)數(shù)組中的位置
- function logParameter(target: Object, propertyName: string, index: number) {
- console.log(target);
- console.log(propertyName);
- console.log(index);
- }
- class Employee {
- greet(@logParameter message: string): string {
- return `hello ${message}`;
- }
- }
- const emp = new Employee();
- emp.greet('hello');
輸入如下圖:
訪問(wèn)器裝飾
使用起來(lái)方式與方法裝飾一致,如下:
- function modification(target: Object, propertyKey: string, descriptor: PropertyDescriptor) {
- console.log(target);
- console.log("prop " + propertyKey);
- console.log("desc " + JSON.stringify(descriptor) + "\n\n");
- };
- class Person{
- _name: string;
- constructor() {
- this._name = 'huihui';
- }
- @modification
- get name() {
- return this._name
- }
- }
裝飾器工廠
如果想要傳遞參數(shù),使裝飾器變成類(lèi)似工廠函數(shù),只需要在裝飾器函數(shù)內(nèi)部再函數(shù)一個(gè)函數(shù)即可,如下:
- function addAge(age: number) {
- return function(constructor: Function) {
- constructor.prototype.age = age
- }
- }
- @addAge(10)
- class Person{
- name: string;
- age!: number;
- constructor() {
- this.name = 'huihui';
- }
- }
- let person = new Person();
執(zhí)行順序
當(dāng)多個(gè)裝飾器應(yīng)用于一個(gè)聲明上,將由上至下依次對(duì)裝飾器表達(dá)式求值,求值的結(jié)果會(huì)被當(dāng)作函數(shù),由下至上依次調(diào)用,例如如下:
- function f() {
- console.log("f(): evaluated");
- return function (target, propertyKey: string, descriptor: PropertyDescriptor) {
- console.log("f(): called");
- }
- }
- function g() {
- console.log("g(): evaluated");
- return function (target, propertyKey: string, descriptor: PropertyDescriptor) {
- console.log("g(): called");
- }
- }
- class C {
- @f()
- @g()
- method() {}
- }
- // 輸出
- f(): evaluated
- g(): evaluated
- g(): called
- f(): called
三、應(yīng)用場(chǎng)景
可以看到,使用裝飾器存在兩個(gè)顯著的優(yōu)點(diǎn):
代碼可讀性變強(qiáng)了,裝飾器命名相當(dāng)于一個(gè)注釋
在不改變?cè)写a情況下,對(duì)原來(lái)功能進(jìn)行擴(kuò)展
后面的使用場(chǎng)景中,借助裝飾器的特性,除了提高可讀性之后,針對(duì)已經(jīng)存在的類(lèi),可以通過(guò)裝飾器的特性,在不改變?cè)写a情況下,對(duì)原來(lái)功能進(jìn)行擴(kuò)展
參考文獻(xiàn)
https://www.tslang.cn/docs/handbook/decorators.html
https://juejin.cn/post/6844903876605280269#heading-5