TypeScript装饰器

本文详细介绍了TypeScript装饰器的概念、基本语法,以及在类、方法、属性和参数上的具体应用,包括类装饰器示例、装饰器工厂、替换类、实例和静态属性装饰、参数装饰以及装饰器执行顺序。同时讨论了装饰器在日志记录、性能监控、权限验证和数据验证等场景的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、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

7、装饰器的应用场景

装饰器在代码中有许多应用场景。以下是一些常见的用例:

日志记录:在方法或类上添加日志功能,用于记录方法的执行过程和结果。

性能监控:在方法或类上添加性能监控功能,用于计算方法的执行时间。

权限验证:在方法或类上添加权限验证功能,用于检查用户是否有权执行某个操作。

数据验证:在方法或类上添加数据验证功能,用于检查输入数据是否合法。

缓存处理:在方法或类上添加缓存处理功能,用于缓存方法的结果。

### TypeScript 装饰器概述 装饰器是一种特殊类型的声明,能够附加到类声明、方法、访问器、属性或参数上。它们使用 `@expression` 这样的语法糖衣形式来实现,其中表达式求值后必须返回一个函数,该函数会在运行时被调用并传入相应的上下文。 为了使装饰器生效,项目配置文件 `tsconfig.json` 中需设置 `"experimentalDecorators": true`[^4]。 ### 类装饰器实例 类装饰器应用于类构造函数,通常用来监视、修改或替换类定义。下面是一个简单的例子: ```typescript function logged(constructor: Function) { console.log('Class:', constructor.name); } @logged class Greeter { greeting: string; constructor(message: string) { this.greeting = message; } greet() { return 'Hello, ' + this.greeting; } } ``` 当创建 `Greeter` 的新实例时,会先执行 `logged` 函数,并打印出类的名字[^1]。 ### 方法装饰器示例 方法装饰器用于修饰原型上的方法。此装饰器接受三个参数:目标对象(对于静态成员来说是类本身;对于实例成员则是类的原型)、成员名字以及描述符。通过这个机制可以在不改动原始逻辑的前提下增强现有行为。 ```typescript // 定义一个简单的方法装饰器 function enumerable(value: boolean) { return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) { descriptor.enumerable = value; }; } class Example { @enumerable(false) private method() { console.log('This is a non-enumerable method'); } } ``` 在这个案例里,`method()` 将不会出现在枚举操作的结果集中,比如 `for...in` 循环中[^3]。 ### 应用场景说明 利用装饰器模式的优势之一在于能够在不影响原代码结构的情况下增加额外的功能。这不仅提高了代码的可维护性和灵活性,同时也增强了程序设计的表现力和清晰度[^5]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值