TypeScript从精通到入门12:装饰器的理解

TypeScript的装饰器是一种特殊类型的声明,它可以附加到类声明、方法、访问符、属性或参数上,以实现对所修饰对象的行为的修改或增强。装饰器主要以函数的形式出现,运行在编译阶段,为开发者提供了一种以声明式方法将元信息添加至已有代码的方式。

若要启用实验性的装饰器特性,你必须在命令行或tsconfig.json里启用experimentalDecorators编译器选项。

作用:通过使用装饰器,开发者可以在不修改代码自身的前提下,给已有代码增加额外的行为(通知),从而解决一些重复性的问题,例如验证、性能监控、数据绑定等。这使得代码更加灵活和可维护。

类装饰器

类型声明:

type ClassDecorator = <TFunction extends Function>
  (target: TFunction) => TFunction | void;
  • @参数:
  1. target: 类的构造器。
  • @返回:
    如果类装饰器返回了一个值,她将会被用来代替原有的类构造器的声明。
  • 因此,类装饰器适合用于继承一个现有类并添加一些属性和方法。

我们定义一个类,我们想在这个类里面加新的属性和功能,但不能修改这块代码!

type Consturctor = { new (...args: any[]): any };

function toString<T extends Consturctor>(BaseClass: T) {
  return class extends BaseClass {
    toString() {
      return JSON.stringify(this);
    }
  };
}

@toString
class C {
  public foo = "foo";
  public num = 24;
}

console.log(new C().toString())

注意  如果你要返回一个新的构造函数,你必须注意处理好原来的原型链。 在运行时的装饰器调用逻辑中 不会为你做这些。

属性装饰器

在Typescript中,属性装饰器可以接收两个参数:

  1. 目标类的原型(对于静态成员则为类的构造函数),
  2. 该属性的名称。

我们可以根据自己的需要来定制属性装饰器。来看以下例子:

function logAccess(target: any, propertyName: string): any {  
    let value = target[propertyName];  
    
    const getter = function () {  
      console.log(`Get ${propertyName}`);  
      return value;  
    };  
    
    const setter = function (newVal: any) {  
      console.log(`Set ${propertyName} to ${newVal}`);  
      value = newVal;  
    };  
    
    // 如果属性原本不可写,则不添加 setter  
    if (delete target[propertyName]) {  
      Object.defineProperty(target, propertyName, {  
        get: getter,  
        set: typeof value === 'object' ? setter : undefined, // 如果值是对象,则允许设置;否则,保持只读  
        enumerable: true,  
        configurable: true  
      });  
    }  
  }  
    
  class MyClass {  
    @logAccess  
    public myProperty: string = '';  
  }  
    
  const obj = new MyClass();  
  obj.myProperty = 'Hello, Decorators!'; // 输出: Set myProperty to Hello, Decorators!
  console.log(obj.myProperty); // 输出: Get myProperty,并打印属性值  

在这个例子中,logAccess 是一个属性装饰器函数。当它被应用到 MyClass 的 myProperty 属性上时,它会创建一个 getter 函数和一个 setter 函数(如果原始属性是可写的)。getter 函数在每次访问属性时打印一条消息,而 setter 函数在每次设置属性值时打印一条消息。通过 Object.defineProperty 方法,我们替换了原始的属性访问器,从而实现了对属性访问的追踪。

参数装饰器

参数装饰器表达式会在运行时当作函数被调用,传入下列3个参数:

  1. 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
  2. 成员的名字。
  3. 参数在函数参数列表中的索引。
function logParameter(target: any, propertyName: string, index: number) {  
  const method = target[propertyName];  
  target[propertyName] = function(...args: any[]) {  
    console.log(`Calling ${propertyName} with parameter at index ${index}: ${args[index]}`);  
    return method.apply(this, args);  
  };  
}  
  
class MyClass {  
  constructor(@logParameter public readonly name: string) {  
  }  
  
  greet(@logParameter name: string) {  
    return `Hello, ${name}!`;  
  }  
}  
  
const obj = new MyClass('Alice'); // 输出: Calling constructor with parameter at index 0: Alice  
console.log(obj.greet('Bob')); // 输出: Calling greet with parameter at index 0: Bob,并返回 'Hello, Bob!'

 在这个例子中,logParameter 是一个参数装饰器函数。它被应用于 MyClass 的构造器参数和 greet 方法的参数上。当构造器或方法被调用时,装饰器函数会首先被触发,并记录传入的参数值。然后,它调用原始的构造器或方法,并返回其结果。

方法装饰器

方法装饰器表达式会在运行时当作函数被调用,传入下列3个参数:

  1. 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
  2. 成员的名字。
  3. 成员的属性描述符

function logMethodCall(target: any, propertyName: string, descriptor: TypedPropertyDescriptor<any>) {  
    const originalMethod = descriptor.value;  
  
    // 修改方法的定义  
    descriptor.value = function(...args: any[]) {  
        console.log(`Calling method ${propertyName} with arguments:`, args);  
        const result = originalMethod.apply(this, args);  
        console.log(`Method ${propertyName} returned:`, result);  
        return result;  
    };  
  
    return descriptor;  
}  
  
class MyClass {  
    @logMethodCall  
    greet(name: string) {  
        return `Hello, ${name}!`;  
    }  
}  
  
const obj = new MyClass();  
obj.greet('Alice'); // 输出调用和返回信息

在这个例子中,logMethodCall 是一个方法装饰器函数。它接收三个参数:target(类的原型),propertyName(被装饰的方法名),以及 descriptor(方法的属性描述符)。装饰器函数内部修改了方法的定义,添加了日志记录的逻辑。当 greet 方法被调用时,会先打印出调用信息和参数,然后执行原始的方法,并打印出返回值。

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值