一文搞懂 TypeScript 裝飾器
前段時間爲了開發 HarmonyOS 路由框架,學習了 TypeScript 裝飾器。在學習中發現無論是官方文檔還是線上資料,鮮有文章可以將裝飾器簡單通俗的表達清楚,故而萌生了撰寫此文的想法。
當然,作爲一個前端門外漢、TS 初學者,寫這篇文字更主要的還是爲了加深自己對裝飾器的理解。新手上路,難免有不足之處,還請各位看官輕噴。
一. 什麼是裝飾器?
官方文檔上的解釋是:
裝飾器是一種特殊類型的聲明,它能夠被附加到類聲明,方法,訪問符,屬性或參數上。裝飾器使用 @表達式這種形式,表達式求值後必須爲一個函數,會在運行時當被調用時,被裝飾的聲明信息做爲參數傳入。
這段官方解釋了幾個信息:
-
裝飾器用在類、屬性、方法或者參數等之上;
注:可修改、替換被裝飾的目標,也可已在不影響原有特性的前提下增加附屬功能。
-
使用 @+ 函數 (或者說表達式) 的形式來書寫;
-
該函數在運行時被調用,函數入參通常爲與被裝飾對象相關的一些信息;
-
這個函數或者不返回值,或者返回一個可以替換被裝飾的目標的對象。(這一點官方解釋裏未說明)
示例代碼如下:
function MyDecorator(target: any) {
...
}
@MyDecorator
class User{
...
}
熟悉Java
的同學,現在應該發現了,這不就是 java 中的註解嗎!不同的是 TypeScript 裝飾器只在運行時被調用,而Java
中的註解能作用於源碼階段、編譯階段和運行時。
二. 裝飾器的分類
裝飾器的分類從寫法上來區分,可分爲:
-
普通裝飾器(不可帶參數)
-
裝飾器工廠(可以帶參數)
從類別上來區分,TypeScript 中裝飾器主要分爲以下五類:
-
類裝飾器 ClassDecorator
-
屬性裝飾器 PropertyDecorator
-
方法裝飾器 MethodDecorator
-
參數裝飾器 ParameterDecorator
-
訪問符裝飾器 AccessorDecorator
前四類裝飾器比較常用,後面的章節我們將分別介紹它們的定義以及實現方法、應用場景。
三. 裝飾器的寫法
這裏用兩段代碼來分別演示普通裝飾器和裝飾器工廠的寫法,它們的主要區別是:「普通裝飾器不可帶參數,而裝飾器工廠可以帶參數。」
3.1 普通裝飾器
直接將函數作爲裝飾器函數,我們稱之爲普通裝飾器。
/**
* 類裝飾器(普通裝飾器寫法)
*/
export function Route(target: object) {
//TODO
...
}
//=========================================================
/**
* 使用 Route 裝飾器:
*/
@Route()
struct DetailPage {
build() {
...
}
}
3.2 裝飾器工廠
裝飾器工廠函數寫法,可以在使用時傳參,例如下面的 routePath 就是入參。
/**
* 類裝飾器(裝飾器工廠實現)
*/
export function Route(routePath: string): ClassDecorator {
return (target: object) => {
//TODO
...
}
}
//=========================================================
/**
* 使用 Route 裝飾器:
*/
@Route({ routePath: '/jump/entry/detail'})
struct DetailPage {
build() {
...
}
}
在裝飾器工廠函數中,首先會執行Route()
,然後在使用返回的匿名器作爲裝飾器的實際邏輯。
三. 裝飾器分類
3.1 類裝飾器(ClassDecorator)
類別裝飾器在類別聲明之前被聲明(緊靠着類別聲明);類別裝飾器檢查類別構造函數,可以用於監視、修改或替換類別定義。
類裝飾器不能用在聲明文件中 (.d.ts),也不能用在任何外部上下文中(比如聲明的類)
類裝飾器表達式會在運行時調用函數,類的構造函數作爲其唯一的參數。
如果類裝飾器返回一個值,它會使用提供的重構函數來替換類的聲明。
簡單來說,類裝飾器是直接作用在類裝飾器上的,類裝飾器的類型描述如下:
type ClassDecorator = <TFunction extends Function>(
target: TFunction
) => TFunction | void;
它的參數只有一個:
target
:對於類裝飾器來說,目標就是這個類本身,或者說是類的構造函數。
示例代碼:
function CustomClassDecorator(info: string): ClassDecorator {
return (target: Function) => {
console.log(target) // [Function user]
console.log(info) // 你好
}
}
@CustomClassDecorator('你好')
class User {
public name!: string
constructor() {
this.name = '馬東錫'
}
}
3.2 屬性裝飾器(PropertyDecorator)
裝飾器屬性聲明在一個屬性聲明之前(緊靠着屬性聲明)。
屬性裝飾器不能用在聲明文件中(.d.ts),或者任何外部上下文(比如聲明的類)裏。
屬性裝飾器表達式會在運行時調用函數,設置以下 2 個參數:
對於靜態成員來說是類的構造函數,對於實例成員來說是類的原型對象。
成員的名字。
屬性裝飾器可以定義在類的屬性上,它的類型描述如下:
type PropertyDecorator = (
target: Object,
propertyName: string | symbol
) => void;
它的參數有兩個:
-
target
:對於屬性裝飾器來說,裝飾器被應用在類的實例屬性上。在這種情況下,目標參數是一個空對象 {},表示在裝飾器內部訪問的對象,即被裝飾類的實例 -
propertyName
:屬性名
示例代碼:
function CustomPropertyDecorator(userName: string): PropertyDecorator {
return (target: Object,
propertyName: string | symbol) => {
console.log(target); // {}
console.log(propertyName); // userName
targetClassPrototype[propertyName] = userName
}
}
class User {
@CustomPropertyDecorator('馬東錫')
public userName!: string
constructor() {
}
}
let user = new User()
console.log(user.userName) // 馬東錫
3.3 方法裝飾器(MethodDecorator)
方法裝飾器聲明在一個方法的聲明(緊靠着方法聲明之前)。它會被應用到方法的屬性聲明上,可以用於監視,修改或者替換方法定義。
裝飾器方法不能用在聲明文件 (.d.ts),重載或者任何外部上下文(比如聲明的類)中;
裝飾器的方法表達式會在運行時調用函數,並確定以下 3 個參數:
對於靜態成員來說是類的構造函數,對於實例成員來說是類的原型對象。
成員的名字。
成員的屬性接口
- 如果方法裝飾器返回一個值,它會被初始化方法的屬性控件。
裝飾器顧名思義就是定義在類中方法上的裝飾器,他的類型描述如下:
type MethodDecorator = <T>(
target: Object,
methodName: string | symbol,
propertyDescriptor: TypedPropertyDescriptor<T>
) => TypedPropertyDescriptor<T> | void;
它接收三個參數:
-
target
:對於方法裝飾器和屬性裝飾器來說,target 參數是一個對象,表示類的原型,其屬性是被裝飾的方法。因此,target 參數實際上是指向了被裝飾方法的引用 -
methodName
: 方法名 -
propertyDescriptor
: 屬性接口
示例代碼如下:
function CustomMethodDecorator(info: string): MethodDecorator {
return (target: Object,
methodName: any,
propertyDescriptor: PropertyDescriptor) => {
console.log(target) // { sayHello: [Function (anonymous)] }
console.log(methodName) //sayHello
let originMethod = propertyDescriptor.value
propertyDescriptor.value = function (...args: any) {
console.log("before")
console.log("我" + info + "來了") //我馬東錫來了
originMethod.call(this, args)
console.log("after")
}
}
}
class User {
@CustomMethodDecorator('馬東錫')
sayHello() {
console.log('執行sayHello()方法)')
}
}
3.4 參數裝飾器 ParammeterDecorator
參數裝飾器聲明在一個參數聲明之前(緊靠着參數聲明)。參數裝飾器檢查類構造函數或方法聲明。
參數裝飾器不能用在聲明文件(.d.ts)中,重載或其他外部上下文(比如聲明的類)裏。
參數裝飾器表達式會在運行時調用函數,確定以下 3 個參數:
對於靜態成員來說是類的構造函數,對於實例成員來說是類的原型對象。
成員的名字。
參數在函數參數列表中的索引。
參數裝飾器只能用於監視一個方法的參數是否被確定。
參數裝飾器的返回值會被忽略。
裝飾器參數定義在方法參數上,其類型描述如下:
type ParameterDecorator = (
target: Object,
methodName: string | symbol,
parameterIndex: number
) => void;
參數裝飾器接收三個參數:
-
target
:意義同方法裝飾器 -
methodName
: -
如果參數修飾器所在的方法爲類的靜態 / 實例方法時,則此參數爲當前參數修飾器所在方法的方法名。
-
如果參數修飾器所在的方法爲類的構造函數參數修飾時,則該參數爲 undefined。
-
parameterIndex
:參數下標
示例代碼:
function CustomParameterDecorator(tag: string): ParameterDecorator {
return (target: any,
methodName: string | symbol,
parameterIndex: number) => {
console.log(tag); // 裝飾實例方法的參數
console.log(target); // { sayHello: [Function (anonymous)] }
console.log(methodName.toString()); // sayHello
console.log(parameterIndex.toString()); // 0
}
}
class User {
constructor() {
}
sayHello(@CustomParameterDecorator("裝飾實例方法的參數") name: String) {
console.log("你好," + name);
}
}
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/KkvXAT3prb3DFfsNuak8gg