JS Decorator
Learned and introduced the basic usage of class decorators
如果你学习或者使用过 Angular 和 NestJs,那么你一定会遇到大量的 @xxx 写法,比如:
Angular 里的组件定义:
ts
@Component({
selector: 'app',
templateUrl: './app.component.html'
})
export class AppComponent {}
又比如 Nest Controller 中
ts
@Controller('users')
export class UserController {
@Get()
findAll() {
return 'all users';
}
}
@xxx 究竟是什么?它们有什么用? 相信java开发者一定不会陌生,其实就是 Decorator 的使用。
什么是 Decorator ?
简单来说,装饰器的本质是函数,可以在类、方法、属性、参数等声明上添加额外的行为或元数据。参考
Decorator 的类型
TypeScript 支持以下几种类型的装饰器:类装饰器,方法装饰器,属性装饰器,参数装饰器
需要注意的是如果你的项目是 TS 项目,需要在 tsconfig 文件中开启编译选项
json
"experimentalDecorators": true,
"emitDecoratorMetadata": true
不然我们的装饰器会编译不过。
类装饰器
作用于类本身,用于观察、修改或替换类定义。
执行时机:在类定义时立即执行,而不是在实例化时执行。它接收的是类的构造函数。
ts
const doc: ClassDecorator = (constructor: Function) => {
constructor.prototype.read = () => {
console.log('I like reading books');
};
}
@doc
class Slim {
constructor() {
console.log('Slim instance created');
}
}
const one: any = new Slim();
one.read();
//Slim instance created
//I like reading books
属性装饰器
作用于类的实例属性或静态属性。
参数target 如果应用于实例属性那么它是类的原型对象, 如果应用于静态属性,则是类本身也就是构造函数。
propertyKey: string | symbol - 被装饰的属性的名称。
熟悉 Object.defineProperty的话,就很好理解了。
ts
const readonly: PropertyDecorator = (target: Object, propertyKey: string | Symbol) => {
Object.defineProperty(target, propertyKey as PropertyKey, {
writable: false,
configurable: false
});
}
class Slim {
@readonly
name: string = 'Slim';
constructor() {
console.log('Slim instance created');
}
}
const one: any = new Slim();
one.name = 'hhh'; //报错
方法装饰器
作用于类的实例方法或静态方法。
参数:
target: 同属性装饰器。
propertyKey: string | symbol - 被装饰的方法的名称。
descriptor: PropertyDescriptor - 方法的属性描述符。这是最关键的一个参数,它包含了方法的 value(方法本身)、writable、enumerable、configurable。比如我们可以通过在装饰器中改写 value实现一个LOG的装饰器:
ts
const Log: MethodDecorator = (target: Object, propertyKey: string | symbol, descriptor: any) => {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`调用 ${String(propertyKey)}, 参数:`, args);
const result = originalMethod.apply(this, args);
console.log(`执行完${String(propertyKey)}:`, result);
return result;
};
return descriptor;
};
class Slim {
name: string;
constructor() {
console.log('Slim instance created');
this.name = 'Slim';
}
@Log
setName(name: string) {
this.name = name;
return "Set name to " + name;
}
}
const one: any = new Slim();
one.setName("Tom");
//Slim instance created
//调用 setName, 参数: [ 'Tom' ]
//执行完setName: Set name to Tom
参数装饰器
作用于类的方法的参数(有点绕口)
参数:
target: 同属性装饰器。
propertyKey: string | symbol | undefined 如果装饰的是方法的参数,这是方法名。如果装饰的是构造函数的参数,则为undefined。
parameterIndex: number - 参数在参数列表中的索引。
ts
const DName: ParameterDecorator = (target, propertyKey, parameterIndex) => {
console.log(`参数名:${String(propertyKey)},参数索引: ${parameterIndex}`);
};
class Slim {
name: string;
constructor() {
console.log('Slim instance created');
this.name = 'Slim';
}
setName(@DName name: string) {
this.name = name;
}
}
const one: any = new Slim();
one.setName("Tom");
//参数名:setName,参数索引: 0
//Slim instance created
Summary
我们对四种装饰器进行了实践,如果你想学习Angular或者Nest的话,Decorator 一定是前置知识。掌握装饰器能帮助我们更好地理解框架,写出更好的代码。
