作用于类的装饰器就是类装饰器 (Class decorators),在 装饰器简介 中最后的装饰器示例就是类装饰器。
1、需要修改构造方法的类装饰器
1.1、不带参数的装饰器
在 装饰器简介 中最后演示的就是修改构造方法的装饰器:
function changePrice<T extends { new(...args: any[]): {} }>(constructor: T) {
return class extends constructor {
price = 666;
};
}
@changePrice
export class Grape {
price: number = 0;
constructor(price: number) {
this.price = price;
console.log(`constructor price : ${this.price}`);
}
}
//组件中调用代码
let grape = new Grape(4);
console.log(grape);
运行结果:
1.2、带参数的装饰器
在其基础上进行修改,可以实现传入参数的类装饰器:
function changePrice<T extends { new(...args: any[]): {} }>(price: number) {
return (constructor: T) => {
let newConstruct = function (...args: any[]) {
let c = class extends constructor {
price = price;
};
return new c(args);
}
let func: any = function (...args: any[]) {
return newConstruct(args);
}
return func;
};
}
@changePrice(8)
export class Grape {
price: number = 0;
constructor(price: number) {
this.price = price;
console.log(`constructor price : ${this.price}`);
}
}
@changePrice(10)
export class Banana {
price: number = 0;
constructor(price: number) {
this.price = price;
console.log(`constructor price : ${this.price}`);
}
}
//组件中调用代码
let grape = new Grape(4);
console.log(grape);
let banana = new Banana(4);
console.log(banana);
通过装饰器参数传入值修改 price 字段,运行结果:
当然,也可以在装饰器中为类添加新的字段:
function changePrice<T extends { new(...args: any[]): {} }>(price: number) {
console.log("price decorator define");
return (constructor: T) => {
console.log("price decorator");
let newConstruct = function (...args: any[]) {
let c = class extends constructor {
price = price;
};
console.log("set price");
return new c(args);
}
let func: any = function (...args: any[]) {
return newConstruct(args);
}
return func;
};
}
function color<T extends { new(...args: any[]): {} }>(color: string) {
console.log("color decorator define");
return (constructor: T) => {
console.log("color decorator");
let newConstruct = function (...args: any[]) {
let c = class extends constructor {
color = color;
};
console.log("set color");
return new c(args);
}
let func: any = function (...args: any[]) {
return newConstruct(args);
}
return func;
};
}
@color("purple")
@changePrice(8)
export class Grape {
price: number = 0;
constructor(price: number) {
this.price = price;
console.log(`constructor price : ${this.price}`);
}
}
//组件中调用代码
let grape = new Grape(4);
console.log(grape);
let grape2 = new Grape(5);
console.log(grape2);
以上代码添加了新的装饰器 color,此装饰器修改目标类的构造方法添加了 color 字段,以上代码还添加了几处打印,用以分析装饰器的作用顺序。运行结果:
可以看到最后打印时对象都添加了 color 字段,通过额外添加的打印可以发现构造方法(newConstruct)外的打印都只打印了一次,是在类转译的时候执行,构造方法(newConstruct)内部的打印每 new 一个对象就会执行一次。装饰器的应用顺序按书写顺序从上到下对应从外到内的顺序执行,上述代码可以理解为:
@color("purple")
(
@changePrice(8)
class Grape{ ... }
)
所以才会有以上的打印顺序。
2、无需修改构造方法的类装饰器
2.1、不带参数的装饰器
如果不需要对类的构造方法进行扩展修改,可以有更简洁的编码格式:
function enableBuy(target: Function): void {
target.prototype.buy = function (count: number) {
console.log(`need money : ${this.price * count} to buy ${count}`);
}
}
@color("purple")
@changePrice(8)
@enableBuy
export class Grape {
price: number = 0;
constructor(price: number) {
this.price = price;
console.log(`constructor price : ${this.price}`);
}
}
//组件中调用代码
let grape: any = new Grape(4);
console.log(grape);
grape.buy(10);
运行代码打印如下:
2.2、带参数的装饰器
添加参数传递后的编码格式:
function enableBuy(coupon:number) { //coupon 打折金额
return (target: Function) => {
target.prototype.buy = function (count: number) {
console.log(`need money ${this.price * count - coupon} to buy ${count}`);
}
}
}
@color("purple")
@changePrice(8)
@enableBuy(5)
export class Grape {
price: number = 0;
constructor(price: number) {
this.price = price;
console.log(`constructor price : ${this.price}`);
}
}
//组件中调用代码
let grape: any = new Grape(4);
console.log(grape);
grape.buy(10);
运行代码打印如下:
类装饰器的使用就介绍到这里,欢迎意见交流。