最近在自学 TypeScript , 发现装饰器这个内容,还是有挺多知识点需要厘清的,因此将学习中的笔记记录如下,方便自查,也希望各路大牛不吝赐教,嘻嘻 (●’◡’●)
ts中的装饰器的分类,大致分为,类装饰器
、属性装饰器
、方法装饰器
、参数装饰器
,下面会对这些装饰器做一下简单的介绍,最后我们再来对比一下这些装饰器的执行顺序。
1. 类装饰器
// 类装饰器
// 可以把逻辑放在装饰器上去实现,主体类部分只实现属性和方法的定义等等
namespace ClassDecorator{ // namespace 命名空间,避免变量或方法污染
function addNameEat(constructor:Function) {
constructor.prototype.name = 'holly';
constructor.prototype.eat = function () {
console.log('eat');
}
}
@addNameEat
class Person{
name!: string;
eat!: Function;
constructor() {}
}
let p:Person = new Person();
console.log(p.name); // holly
p.eat(); // eat
}
// 类装饰器工厂
// 可以实现装饰器中接受参数,原理与装饰器一样,都是把逻辑放在装饰器上去实现,主体类部分只实现变量定义等等
// 而且不论是利用类装饰器还是类装饰器工厂实现的属性跟方法,都是挂载在原型上的
namespace ClassDecoratorFatoty { // 命名空间 可以避免变量名冲突
function addNameEatFactory(name: string) {
return function addNameEat(constructor:Function) {
constructor.prototype.name = name;
constructor.prototype.eat = function () {
console.log('eat');
}
}
}
@addNameEatFactory('jiagou')
class Person{
name!: string;
eat!: Function;
constructor() {}
}
let p:Person = new Person();
console.log(p.name); // jiagou
p.eat(); // eat
}
// 类装饰器还可以用作替换类
namespace ClassDecoratorReplace{
// 出于类型安全考虑 替换的类中的属性或者方法,只能多不能少
function replaceClass(constructor: Function) {
return class{
name!: string;
eat!: Function;
age!: number;
}
}
@replaceClass
class Person{
name!: string;
eat!: Function;
constructor() {}
}
let p:Person = new Person();
console.log(p.name);
}
属性装饰器
// 属性装饰器 在运行时会被当做函数被调用
// 可以装饰属性 也可以装饰方法
namespace PropertyDecorator {
// 装饰实例属性时,target是构造函数的原型 propertyKey要装饰的属性
function upperCase(target: any, propertyKey: string) {
// console.log(target); // Person {}
// console.log(propertyKey); // name
let value = target[propertyKey]; // 获取到name值
const getter = () => value;
const setter = (newVal:string) => {value = newVal.toUpperCase()}
if(delete target[propertyKey]) {
Object.defineProperty(target, propertyKey, {
get: getter,
set: setter,
enumerable: true,
configurable: true
});
}
}
// 装饰静态属性时,target是构造函数本身
function staticPropertyDecorator(target: any, propertyKey: string) {
// console.log('装饰静态属性的装饰器', target); // [Function: Person]
}
function noEnumerable(target: any, propertyKey: string, descriptor:PropertyDescriptor) {
// console.log(target, propertyKey);
descriptor.enumerable = false; // 修改实例的可枚举属性
}
function toNumber(target: any, propertyKey: string, descriptor:PropertyDescriptor) {
/** descriptor打印出来的信息
* {
value: [Function: sum],
writable: true,
enumerable: false,
configurable: true
}
*/
console.log(descriptor);
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 = 'holly'; // 实例属性
@staticPropertyDecorator
static age: number; // 静态属性
@noEnumerable
getName() { console.log(this.name); } // 实例方法
@toNumber
sum(...args: any[]) {
return args.reduce((acc: number, cur: number) => acc+cur, 0);
}
}
let p: Person = new Person();
console.log(p.name); // HOLLY
console.log(p.sum('1', '2', '3', '4')); // 10
}
参数装饰器
namespace ParamDecorator {
// target 如果是静态成员就是构造函数本身 非静态成员就是构造函数的原型
// methoName 方法名称
// paramsIndex 参数的索引
function addPwd(target: any, methodName:any, paramsIndex: number){
// 这里的target是构造函数的原型
console.log(target, methodName, paramsIndex); // Person {} login 1
target.age = 10;
}
class Person {
age!: number;
login(username: string, @addPwd password: string){
console.log(this.age, username, password);
}
}
let p: Person = new Person();
p.login('holly', '12434'); // 10 holly 12434
}
装饰器的执行顺序
// 装饰器的执行顺序
namespace DecoratorSort {
// 类装饰器
function ClassDecorator1() {
return function(target: any) {
console.log('ClassDecorator1', target);
}
}
function ClassDecorator2() {
return function(target: any) {
console.log('ClassDecorator2', target);
}
}
function PropertyDecorator(name: string) {
return function(target: any, propertyKey: string) {
console.log('PropertyDecorator', target, propertyKey);
}
}
function MethodDecorator() {
return function(target: any, propertyKey: string) {
console.log('MethodDecorator', propertyKey, target);
}
}
function ParamterDecorator() {
return function(target: any, methodName: string, index: number) {
console.log('ParamterDecorator', methodName, index);
}
}
@ClassDecorator1()
@ClassDecorator2()
class Person {
@PropertyDecorator('name')
name!: string;
@PropertyDecorator('age')
age: number = 10;
@MethodDecorator()
hello(@ParamterDecorator() p1: string, @ParamterDecorator() p2: string){
console.log(p1, p2);
}
}
}
/**
* 输出顺序
* PropertyDecorator Person {} name
PropertyDecorator Person {} age
ParamterDecorator hello 1
ParamterDecorator hello 0
MethodDecorator hello Person {}
ClassDecorator2 [Function: Person]
ClassDecorator1 [Function: Person]
规律总结:
1. 类装饰器是最后执行的,而且越靠近类的装饰器越先执行
2. 方法和方法参数中的装饰器 是先执行参数 再执行方法
3. 方法和属性装饰器,是谁在前,就先执行谁
*/