一:類(lèi)的裝飾器:是一種與類(lèi)(class)相關(guān)的語(yǔ)法,用來(lái)注釋或修改類(lèi)和類(lèi)方法,裝飾器本身是一個(gè)函數(shù),裝飾器通過(guò)@來(lái)使用.
//注意裝飾器的執(zhí)行時(shí)機(jī),不是在 創(chuàng)建實(shí)例的時(shí)候運(yùn)行,而是在類(lèi)創(chuàng)建的時(shí)候就會(huì)執(zhí)行。
function testDec(constructor: any) {
console.log("123");
}
@testDec
class Person {}
//裝飾器對(duì)類(lèi)的行為的改變,是代碼編譯時(shí)發(fā)生的,而不是在運(yùn)行時(shí)。這意味著,裝飾器能在編譯階段運(yùn)行代碼。也就是說(shuō),裝飾器本質(zhì)就是編譯時(shí)執(zhí)行的函數(shù)。
//所以:這里類(lèi)Person創(chuàng)建完成即會(huì)打印log:123
多個(gè)類(lèi)的裝飾器同時(shí)使用:
//從上至下(從左至右)收集裝飾器,但是執(zhí)行的時(shí)候,先收集的后執(zhí)行
function testDec(constructor: any) {
console.log(123);
}
function testDec2(constructor: any) {
console.log(456);
}
@testDec
@testDec2
class People {}
//456
//123
注意,使用裝飾器時(shí),需要修改ts的配置文件
ts中使用裝飾器注意修改配置文件這兩項(xiàng).png
當(dāng)我們需要對(duì)裝飾器的執(zhí)行進(jìn)行控制的時(shí)候,即有的時(shí)候希望裝飾器執(zhí)行,有的時(shí)候不希望他執(zhí)行,這種時(shí)候需要傳遞參數(shù)來(lái)進(jìn)行控制.
function dec(flag: boolean) {
return function (constructor: any) {
if (flag) {
//我們可以對(duì)constructor進(jìn)行拓展
return constructor.prototype.getName = () =>{
console.log("dell");
}
} else {
console.log(222);
}
};
}
@dec(true)
class Color {
constructor(private name: string) {}
}
//log輸出 111
當(dāng)我們通過(guò)prototype對(duì)類(lèi)進(jìn)行拓展時(shí)存在下面的問(wèn)題,實(shí)例化對(duì)象上不找不到拓展的方法(屬性)
function testDecorator(constructor: any) {
constructor.prototype.getName = () => {
return "lee";
};
}
@testDecorator
class Test {
constructor(public name: string) {}
}
const test = new Test("lili");
test.getName();
//可以通過(guò)這種方式來(lái)解決,但是不符合ts的思想
(test as any).getName();

未解決上圖的問(wèn)題,我們對(duì)代碼進(jìn)行優(yōu)化,用工廠(chǎng)方法對(duì)裝飾器進(jìn)行封裝,返回一個(gè)裝飾過(guò)的類(lèi)
//這里做一層封裝,返回一個(gè)裝飾器
function testDecorator() {
//泛型,一個(gè)可以接收多個(gè)參數(shù)的構(gòu)造函數(shù)
return function <T extends new (...args: any[]) => any>(constructor: T) {
return class extends constructor {
name = "lee";
getName() {
return this.name;
}
};
};
}
//獲取裝飾器裝飾過(guò)的類(lèi)
const Test = testDecorator()(
class {
constructor(public name: string) {}
}
);
//此時(shí)可以調(diào)用getName方法
const test = new Test("lili");
console.log(test.getName());
二:方法的裝飾器
//普通函數(shù)的target:類(lèi)的prototype原型
//靜態(tài)函數(shù)的target:類(lèi)的構(gòu)造函數(shù)
function funcDec(target: any, key: string, descriptor: PropertyDescriptor) {
console.log(target, key);
//descriptor.writable = false;//設(shè)置是否可修改
descriptor.value= ()=>{
return "newName"
}
}
class Question {
name: string;
constructor(name: string) {
this.name = name;
}
@funcDec
getName() {
return this.name;
}
}
const question = new Question("問(wèn)題");
console.log(Question.prototype);
question.getName();

語(yǔ)法:Object.defineProperty(obj,property,descriptor)
參數(shù)一:obj
綁定屬性的目標(biāo)對(duì)象
參數(shù)二:property
綁定的屬性名
參數(shù)三:descriptor
屬性描述(配置),且此參數(shù)本身為一個(gè)對(duì)象
屬性值1:value
設(shè)置屬性默認(rèn)值
屬性值2:writable
設(shè)置屬性是否能夠修改
屬性值3:enumerable
設(shè)置屬性是否可以枚舉,即是否允許遍歷
屬性值4:configurable
設(shè)置屬性是否可以刪除或編輯屬性值5:get
獲取屬性的值
屬性值6:set
設(shè)置屬性的值
三:訪(fǎng)問(wèn)器的裝飾器
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"; //觸發(fā)set
console.log(book.name); //觸發(fā)get

四.屬性的訪(fǎng)問(wèn)器
//屬性訪(fǎng)問(wèn)器只能接收兩個(gè)參數(shù),需要手動(dòng)的返回descriptor
function nameDec(target: any, key: string): any {
//這里修改的并不是實(shí)例上的name,而是原型上的name
target[key] = "第一行代碼";
const descriptor: PropertyDescriptor = {
writable: true,
};
return descriptor;
}
//類(lèi)中的屬性name是放在實(shí)例上的
class Book {
@nameDec
name: string;
constructor(name: string) {
this.name = name;
}
}
const book = new Book("三體");
book.name = "三體2";
console.log(book.name);
五:參數(shù)上的裝飾器
//類(lèi)的原型 方法名 參數(shù)所在的位置
function paramDec(target: any, key: string, paramsIndex: number) {
console.log(target, key, paramsIndex);
}
class Book {
getBookName(@paramDec name: string, price: number) {
return `書(shū)名:${name},價(jià)格是:${price}`;
}
}
//Book {} getBookName 0
六.裝飾器的簡(jiǎn)單應(yīng)用
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 不存在
上面的方法能夠成功的捕獲到錯(cuò)誤,但是當(dāng)存在大量的屬性需要try catch時(shí),上面的方式過(guò)于復(fù)雜,復(fù)用性太低.這個(gè)時(shí)候我們可以用裝飾器來(lái)進(jìn)行統(tǒng)一的捕獲錯(cuò)誤的處理.
//為了給出精確的提示,我們用工廠(chǎng)方法進(jìn)行封裝個(gè)
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 不存在
