一、裝飾器的作用
我個人的理解是:ts
中的裝飾器類似於 Java
語言中的註解,對於使用者來說都是為類和屬性等程式碼元素
新增額外的功能,而不改變程式碼元素
原有的結構。例如在 Java
中我們用的比較多的 Spring
框架中的註解 @Component
可以將一個類放置到 IoC
容器中進行託管,使用 @Autowired
來取出該物件進行使用等等。
二、類裝飾器
1. 普通裝飾器
先寫一個最簡單的類裝飾器,它看起來就是一個方法:
function logClass(params:any) {
console.log(params);
}
使用方式如下,非常類似Java
的註解:
@裝飾器名
class 被裝飾的類 {
...
}
然後我們編寫一個類來進行裝飾:
@logClass // <-- 類裝飾器的使用方式
class HttpClient {
constructor() {
}
getData() {
}
}
檢視執行的結果:
可以看出,logClass
引數 params
中得到的是修飾的類(在console
中打印出型別是方法的原因是ts
中的類在編譯成js
程式碼後,會變成一個函式)
為類擴充套件屬性和方法
擴充套件屬性
這裡為被修飾的類新增一個 info
屬性:
function logClass(params:any) {
// 在原型鏈上新增附加屬性
params.prototype.info = '附加資訊'
}
然後我們將這個屬性打印出來:
let httpClient:any = new HttpClient();
console.log(httpClient.info);
執行結果:
tips:注意這裡的建立的物件的型別需要指定為
any
才可以使用info
屬性
擴充套件方法
同理,我們也可以為被修飾的類擴充套件方法:
function logClass(params:any) {
params.prototype.run = function() {
console.log("我是擴充套件的run方法");
}
}
方法呼叫:
let httpClient:any = new HttpClient();
httpClient.run();
執行結果:
使用裝飾器修改屬性和重寫方法
語法:
function 裝飾器名(target:any) {
return class extends target {
原有的某屬性:any = "我是修改後的資料";
原有的某方法() {
//...重寫的內容
}
}
}
示例:
function logClass(target:any) {
return class extends target {
apiUrl:any = "我是修改後的資料";
getData() {
this.apiUrl += "-----";
console.log(this.apiUrl);
}
}
}
被修飾的類:
@logClass
class HttpClient {
public apiUrl: string | undefined;
constructor() {
}
getData() {
}
}
執行程式碼:
let httpClient:HttpClient = new HttpClient();
httpClient.getData();
執行結果:
可以看到,使用裝飾器,我們不需要編寫子類即可修改類的屬性和重寫類的方法了!
2. 裝飾器工廠
當我們使用普通裝飾器時可以發現,我們是無法傳遞引數的,而現在要學的裝飾器工廠則可以傳遞引數。
語法格式:
function 裝飾器名(引數列表) {
return function(target: any) {
...
}
}
現在引數列表
中的是我們輸入的引數,而 target
是被裝飾的類。
示例:
function logClass(params:string, p2?:string) {
return function(target: any) {
console.log("params",params);
console.log("p2",p2);
console.log("target",target);
}
}
被裝飾的類:
@logClass("hello world", "第二個引數")
class HttpClient {
constructor() {
}
getData() {
}
}
執行結果:
三、屬性裝飾器
定義方式:
function 屬性裝飾器名() {
return function(target: any, attr: any) {
...
}
}
這裡返回的 function
有兩個引數:
- 對於靜態成員來說是類的
建構函式
,對於例項成員來說是類的原型物件
- 成員的名字(
string
型別)
例如我們可以使用下面的方式使用屬性裝飾器來修改屬性:
屬性裝飾器:
function logProperty(params: any) {
return function(target: any, attr: any) {
target[attr] = params;
}
}
被裝飾的類:
// @logClass
class HttpClient {
@logProperty("hello world")
public apiUrl: string | undefined;
constructor() {
}
getData() {
}
}
列印屬性:
let httpClient:HttpClient = new HttpClient();
console.log(httpClient.apiUrl);
執行結果:
可以看到屬性的值被修改為了我們傳入到註解中的值了。
四、方法裝飾器
定義方式:
function 裝飾器名(引數列表) {
return function(target:any, methodName:any, desc:any) {
...
}
}
這裡返回的 function
有三個引數:
target
是修飾的類的物件原型methodName
是方法名desc
方法的描述資訊物件
我們可以將它們打印出來:
function Get(url:string) {
return function(target:any, methodName:any, desc:any) {
console.log(target);
console.log(methodName);
console.log(desc);
}
}
class HttpClient {
constructor() {
}
@Get("http://www.itying.com")
getData() {
}
}
執行結果:
使用方法裝飾器對方法進行擴充套件
被修飾類:
class HttpClient {
constructor() {
}
getData(...args:any[]) {
console.log(args);
}
}
此時我們希望擴充套件 getData()
方法的功能:在執行前後進行日誌列印,並將傳入的引數先轉換為 string
型別。
具體的裝飾器如下:
function logMethod() {
return function(target:any, methodName:any, desc:any) {
// 先儲存原來的函式
let originMethod:Function = desc.value;
// 修改函式的實現
desc.value = function(...args:any[]) {
// 資料預處理(轉為string)
args = args.map(value=>String(value));
// 執行前日誌
console.log(`====函式${methodName}執行前====`);
// 呼叫原函式
originMethod.apply(this, args);
// 執行後日志
console.log(`====函式${methodName}執行後====`);
}
}
}
建立物件並呼叫方法:
let httpClient:HttpClient = new HttpClient();
httpClient.getData(1,2,3);
執行結果:
五、方法引數裝飾器
定義方法:
function logParam(){
return function(target:any, methodName:any,paramIndex:any) {
...
}
}
定義方法和方法裝飾器基本一致,區別在於三個引數不同:
target
是修飾的類的物件原型methodName
是方法名paramIndex
是引數的索引
示例:
function logParam(){
return function(target:any, methodName:any,paramIndex:any) {
console.log(target);
console.log(methodName);
console.log(paramIndex);
}
}
被修飾的類
class HttpClient {
// @logProperty("hello world")
public apiUrl: string | undefined;
constructor() {
}
getData(@logParam() str:string) {
console.log("getData函式被呼叫了");
}
}
執行結果:
六、裝飾器的執行順序
屬性裝飾器 => 方法裝飾器 => 方法引數裝飾器 => 類裝飾器