一:類的裝飾器:是一種與類(class)相關的語法,用來注釋或修改類和類方法,裝飾器本身是一個函數,裝飾器通過@來使用.
//注意裝飾器的執行時機,不是在 創建實例的時候運行,而是在類創建的時候就會執行。
function testDec(constructor: any) {
console.log("123");
}
@testDec
class Person {}
//裝飾器對類的行為的改變,是代碼編譯時發生的,而不是在運行時。這意味著,裝飾器能在編譯階段運行代碼。也就是說,裝飾器本質就是編譯時執行的函數。
//所以:這里類Person創建完成即會打印log:123
多個類的裝飾器同時使用:
//從上至下(從左至右)收集裝飾器,但是執行的時候,先收集的后執行
function testDec(constructor: any) {
console.log(123);
}
function testDec2(constructor: any) {
console.log(456);
}
@testDec
@testDec2
class People {}
//456
//123
注意,使用裝飾器時,需要修改ts的配置文件
ts中使用裝飾器注意修改配置文件這兩項.png
當我們需要對裝飾器的執行進行控制的時候,即有的時候希望裝飾器執行,有的時候不希望他執行,這種時候需要傳遞參數來進行控制.
function dec(flag: boolean) {
return function (constructor: any) {
if (flag) {
//我們可以對constructor進行拓展
return constructor.prototype.getName = () =>{
console.log("dell");
}
} else {
console.log(222);
}
};
}
@dec(true)
class Color {
constructor(private name: string) {}
}
//log輸出 111
當我們通過prototype對類進行拓展時存在下面的問題,實例化對象上不找不到拓展的方法(屬性)
function testDecorator(constructor: any) {
constructor.prototype.getName = () => {
return "lee";
};
}
@testDecorator
class Test {
constructor(public name: string) {}
}
const test = new Test("lili");
test.getName();
//可以通過這種方式來解決,但是不符合ts的思想
(test as any).getName();
裝飾過的類上找不到拓展的方法.png
未解決上圖的問題,我們對代碼進行優化,用工廠方法對裝飾器進行封裝,返回一個裝飾過的類
//這里做一層封裝,返回一個裝飾器
function testDecorator() {
//泛型,一個可以接收多個參數的構造函數
return function <T extends new (...args: any[]) => any>(constructor: T) {
return class extends constructor {
name = "lee";
getName() {
return this.name;
}
};
};
}
//獲取裝飾器裝飾過的類
const Test = testDecorator()(
class {
constructor(public name: string) {}
}
);
//此時可以調用getName方法
const test = new Test("lili");
console.log(test.getName());
二:方法的裝飾器
//普通函數的target:類的prototype原型
//靜態函數的target:類的構造函數
function funcDec(target: any, key: string, descriptor: PropertyDescriptor) {
console.log(target, key);
//descriptor.writable = false;//設置是否可修改
descriptor.value= ()=>{
return "newName"
}
}
class Question {
name: string;
constructor(name: string) {
this.name = name;
}
@funcDec
getName() {
return this.name;
}
}
const question = new Question("問題");
console.log(Question.prototype);
question.getName();
descriptor參考js的Object.defineProperty方法.png
語法:Object.defineProperty(obj,property,descriptor)
參數一:obj
綁定屬性的目標對象
參數二:property
綁定的屬性名
參數三:descriptor
屬性描述(配置),且此參數本身為一個對象
屬性值1:value
設置屬性默認值
屬性值2:writable
設置屬性是否能夠修改
屬性值3:enumerable
設置屬性是否可以枚舉,即是否允許遍歷
屬性值4:configurable
設置屬性是否可以刪除或編輯屬性值5:get
獲取屬性的值
屬性值6:set
設置屬性的值
三:訪問器的裝飾器
function visitDec(target: any, key: string, descriptor: PropertyDescriptor) {
//descriptor.configurable = false;
}
class Book {
private _name: string;
constructor(name: string) {
this._name = name;
}
@visitDec
get name() {
return this._name;
}
set name(name: string) {
this._name = name;
}
}
const book = new Book("三體");
book.name = "三體2"; //觸發set
console.log(book.name); //觸發get
注意.png
四.屬性的訪問器
//屬性訪問器只能接收兩個參數,需要手動的返回descriptor
function nameDec(target: any, key: string): any {
//這里修改的并不是實例上的name,而是原型上的name
target[key] = "第一行代碼";
const descriptor: PropertyDescriptor = {
writable: true,
};
return descriptor;
}
//類中的屬性name是放在實例上的
class Book {
@nameDec
name: string;
constructor(name: string) {
this.name = name;
}
}
const book = new Book("三體");
book.name = "三體2";
console.log(book.name);
五:參數上的裝飾器
//類的原型 方法名 參數所在的位置
function paramDec(target: any, key: string, paramsIndex: number) {
console.log(target, key, paramsIndex);
}
class Book {
getBookName(@paramDec name: string, price: number) {
return `書名:${name},價格是:${price}`;
}
}
//Book {} getBookName 0
六.裝飾器的簡單應用
const BookInfo: any = undefined;
class Book {
getBookName() {
try {
return BookInfo.name;
} catch (e) {
console.log("BookInfo -- name 不存在");
}
}
getBookPrice() {
try {
return BookInfo.price;
} catch (e) {
console.log("BookInfo --- price 不存在");
}
}
}
const book = new Book();
book.getBookName(); //BookInfo -- name 不存在
book.getBookPrice(); //BookInfo --- price 不存在
上面的方法能夠成功的捕獲到錯誤,但是當存在大量的屬性需要try catch時,上面的方式過于復雜,復用性太低.這個時候我們可以用裝飾器來進行統一的捕獲錯誤的處理.
//為了給出精確的提示,我們用工廠方法進行封裝個
function catchError(msg: string) {
return function (target: any, key: string, descriptor: PropertyDescriptor) {
let fn = descriptor.value;
descriptor.value = () => {
try {
fn();
} catch (e) {
console.log(msg);
}
};
};
}
const BookInfo: any = undefined;
class Book {
@catchError("BookInfo -- name 不存在")
getBookName() {
return BookInfo.name;
}
@catchError("BookInfo -- price 不存在")
getBookPrice() {
return BookInfo.price;
}
}
const book = new Book();
book.getBookName(); //BookInfo -- name 不存在
book.getBookPrice(); //BookInfo --- price 不存在