目录
一、TypeScript 装饰器介绍
装饰器是一种通过添加标注
的方式来对类型
进行扩展
的工具。它可以让我们在类
、方法
、属性
、参数
等各种数据上进行扩展,从而提高代码的可读性
和扩展性
。本文将介绍 TypeScript 装饰器的基本语法和具体使用方法。
1. 装饰器的作用
- 只能在类中使用
- 减少冗余代码量
- 提高代码扩展性
2. 装饰器的语法
装饰器本质上就是一个函数,在特定的位置调用装饰器函数即可对数据进行扩展。下面是一个装饰器函数的基本语法:
function myDecorator(target: any) {
// 对 target 进行处理
}
其中,target
表示要扩展的数据,可以是类
、方法
、属性
、参数
等。
二、装饰器的具体使用方法
1. 类装饰器
- 在类上添加 name 和 eat 属性
namespace a {
function addNameEat(constructor: Function):void {
constructor.prototype.name = "tom";
constructor.prototype.eat = () => {};
};
@addNameEat
class Person {
name: string;
eat: Function;
constructor() {}
}
let p: Person = new Person();
console.log(p.name); // tom
p.eat();
}
2. 装饰器工厂
- 通过装饰器工厂传递参数
namespace b {
function addNameEatFactory(name: string):Function {
return function addNameEat(constructor: Function):void {
constructor.prototype.name = name;
constructor.prototype.eat = () => {};
};
};
@addNameEatFactory('Jerry')
class Person {
name: string;
eat: Function;
constructor() {}
}
let p: Person = new Person();
console.log(p.name); // Jerry
p.eat();
}
3. 装饰器替换类
- 使用装饰器替换类
namespace c {
function replaceClass(constructor: Function) {
return class {
// 由于类型安全,此处的属性只能多不能少
name: string = 'tom';
eat: Function = () => {};
constructor() {}
}
};
@replaceClass
class Person {
name: string;
eat: Function;
constructor() {}
}
let p: Person = new Person();
console.log(p.name); // tom
p.eat();
}
4. 属性、方法装饰器
- 属性、方法装饰器
namespace d {
// 如果装饰的是实例属性的话,target是构造函数的原型
function upperCase(target: any, propertyKey: string) {
// console.log(target, propertyKey); // { getName: [Function (anonymous)], sun: [Function (anonymous)] } name
let value = target[propertyKey]
const getter = () => value;
const setter = (newVal: string) => { value = val.toUpperCase(); };
if (delete target[propertyKey]) {
Object.defineProperty(target, propertyKey, {
get: getter,
set: setter,
enumerable: true,
configurable: true
});
}
}
// 如果装饰的是静态属性,target就是构造函数
function staticPrototypeDecorator(target: any, propertyKey: string) {
// console.log(target, propertyKey); // [Function: Person] { age: 18 } age
}
function noEnumerable(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
descriptor.enumerable = false; // 不可枚举
}
function toNumber(target: any, propertyKey, descriptor: PropertyDescriptor) {
let oldMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
args = args.map(item => parseFloat(item));
return oldMethod.apply(this, args)
}
}
class Person {
@upperCase
name: string = 'jerry'; // 实例属性
@staticPrototypeDecorator
static age: number = 18; // 静态属性
@noEnumerable
getName() { console.log(this.name); }; // 实例方法
@toNumber
sum(...args: any[]) { // 实例方法
return args.reduce((prev: number, next: number) => prev + next, 0);
}
}
let p = new Person();
console.log(p.name); // JERRY
console.log(p.sum('1', '2', '3', '4')); // 10
}
5. 参数装饰器
- 参数装饰器
namespace e {
// target:静态属性指构造函数,实例属性、实例方法值构造函数的原型
// methodName:方法的名称
// paramIndex:参数的索引
function addAge(target: any, methodName: string, paramIndex: number) {
// console.log(target, methodName, paramIndex); // { login: [Function (anonymous)] } login 1
target.age = 18;
}
class Person {
age: number;
login(username: string, @addAge password: string) {
console.log(this.age, username, password); // 18 admin admin123
}
}
let p = new Person();
p.login('admin', 'admin123');
}
6. 装饰器执行顺序
- class装饰器最后执行,后写的类装饰器先执行
- 方法和参数中的装饰器,参数装饰器先执行,再执行方法装饰器
- 方法和属性装饰器,谁在前面先执行谁
- 先内后外 先上后下执行
namespace f {
function ClassDecorator1() {
return function (target) {
console.log('ClassDecorator1');
}
}
function ClassDecorator2() {
return function (target) {
console.log('ClassDecorator2');
}
}
function PropertyDecorator(name: string) {
return function (target, propertyName) {
console.log('PropertyDecorator', propertyName, name);
}
}
function MethodDecorator() {
return function (target, propertyName) {
console.log('MethodDecorator', propertyName);
}
}
function ParameterDecorator() {
return function (target, propertyName, index) {
console.log('ParameterDecorator', propertyName, index);
}
}
@ClassDecorator1()
@ClassDecorator2()
class Person {
@PropertyDecorator('name')
name: string = '';
@PropertyDecorator('age')
age: string = '';
@MethodDecorator()
hello(@ParameterDecorator() hello: string, @ParameterDecorator() word: string) {}
}
}
输出顺序为:
// 打印顺序
PropertyDecorator name name
PropertyDecorator age age
ParameterDecorator hello 1
ParameterDecorator hello 0
MethodDecorator hello
ClassDecorator2
ClassDecorator1
总结
本文简单介绍了 TypeScript 装饰器的基本语法和使用方法。通过装饰器,我们可以大大提高代码的可读性和扩展性,减少冗余代码量,同时也实现了更好的代码复用。如果您有任何问题或建议,请告诉我。下次再见