Skip to content
On this page

02 装饰器

1.ES6基础

1.1 原型链

原型链 这张图展示了 JavaScript 中函数和原型对象之间的关系,揭示了它们在原型链上的互相连接。通过这个图来理解 JavaScript 中的对象和函数是如何通过原型链连接在一起的 主要部分

  1. Functions:
  • 表示函数对象,图中展示了Foo、Object 和 Function对象
  • 函数对象通过 prototype 属性指向其原型对象
  1. Prototypes(instances):
  • 表示由函数对象创建的实例对象,通过 new 操作符创建。
  • 原型对象有一个 constructor 属性指向创建它的函数对象,并通过 proto 属性链接到原型链上的下一个对象。 详细解析
  1. 函数Foo()和示例f1
  • f1 = new Foo() 创建了 f1 实例。
  • f1 的 proto 属性指向 Foo.prototype。
  • Foo 函数对象的 prototype 属性指向 Foo.prototype。
  • Foo.prototype 的 constructor 属性指向 Foo 函数对象。
  1. 函数 Object() 和实例 o1、o2:
  • o1 = new Object() 和 o2 = new Object() 创建了 o1 和 o2 实例。
  • o1 和 o2 的 proto 属性指向 Object.prototype。
  • Object 函数对象的 prototype 属性指向 Object.prototype。
  • Object.prototype 的 constructor 属性指向 Object 函数对象。
  • Object.prototype.proto 指向 null,表示原型链的终点。
  1. 函数 Function() 和函数对象 Foo、Object:
  • Foo 和 Object 函数对象是由 Function 构造函数创建的。
  • Foo.proto 和 Object.proto 指向 Function.prototype。
  • Function 函数对象的 prototype 属性指向 Function.prototype。
  • Function.prototype 的 constructor 属性指向 Function 函数对象。
  • Function.prototype.proto 指向 Object.prototype,连接到一般对象的原型链上。

1.2. Reflect

Reflect 是 ES6 中引入的一个内置对象,它提供了一些反射方法,这些方法与那些在 Object 和 Function 原型上的方法具有相同的名称和功能。Reflect 的引入主要是为了使操作对象的行为变得更规范和一致,并且提供一个与 Proxy 对象互补的 API。下面是对 Reflect 的详细讲解。

1.2.1 Reflect 的方法

Reflect 对象的方法大致可以分为三类:对象操作、函数调用和原型操作。以下是 Reflect 所提供的所有方法及其说明:

对象操作方法

  • Reflect.get(target, propertyKey[, receiver]):获取对象的属性值,相当于 target[propertyKey]。
  • Reflect.set(target, propertyKey, value[, receiver]):设置对象的属性值,相当于 target[propertyKey] = value。
  • Reflect.deleteProperty(target, propertyKey):删除对象的属性值,相当于 delete target[propertyKey]。
  • Reflect.has(target, propertyKey):检查对象是否有某个属性,相当于 propertyKey in target。
  • Reflect.defineProperty(target, propertyKey, descriptor):定义对象的属性,相当于 Object.+ defineProperty(target, propertyKey, descriptor)。
  • Reflect.getOwnPropertyDescriptor(target, propertyKey):获取对象自有属性的描述符,相当于 Object.+ getOwnPropertyDescriptor(target, propertyKey)。
  • Reflect.ownKeys(target):返回对象的所有自有属性的键,相当于 Object.getOwnPropertyNames(target).+ concat(Object.getOwnPropertySymbols(target))。

函数调用方法

  • Reflect.apply(target, thisArgument, argumentsList):调用一个函数,相当于 Function.prototype.apply.+ call(target, thisArgument, argumentsList)。
  • Reflect.construct(target, argumentsList[, newTarget]):构造一个实例,相当于 new target(...argumentsList)。

原型操作方法

  • Reflect.getPrototypeOf(target):获取对象的原型,相当于 Object.getPrototypeOf(target)。
  • Reflect.setPrototypeOf(target, prototype):设置对象的原型,相当于 Object.setPrototypeOf(target, + prototype)。
  • Reflect.isExtensible(target):检查对象是否是可扩展的,相当于 Object.isExtensible(target)。 Reflect.preventExtensions(target):让一个对象变得不可扩展,相当于 Object.preventExtensions(target)。

1.2.2 具体示例

1.2.2.1 Reflect.get 和 Reflect.set
js
const obj = { a: 1 };

// 获取对象的属性值
console.log(Reflect.get(obj, 'a')); // 1

// 设置对象的属性值
Reflect.set(obj, 'b', 2);
console.log(obj.b); // 2

1.2.2.2 Reflect.deleteProperty

js
const obj = { a: 1, b: 2 };

// 删除对象的属性
Reflect.deleteProperty(obj, 'a');
console.log(obj); // { b: 2 }

1.2.2.3 Reflect.has

js
const obj = { a: 1 };

// 检查对象是否有某个属性
console.log(Reflect.has(obj, 'a')); // true
console.log(Reflect.has(obj, 'b')); // false

1.2.2.4 Reflect.defineProperty

js
const obj = {};

// 定义对象的属性
Reflect.defineProperty(obj, 'a', {
  value: 1,
  writable: true,
  enumerable: true,
  configurable: true
});
console.log(obj.a); // 1

1.2.2.5 Reflect.apply

js
function sum(a, b) {
  return a + b;
}

// 调用函数
console.log(Reflect.apply(sum, undefined, [1, 2])); // 3
1.2.2.6 Reflect.construct
js
function Person(name) {
  this.name = name;
}

// 构造实例
const person = Reflect.construct(Person, ['John']);
console.log(person.name); // John
1.2.2.7 Reflect.getPrototypeOf 和 Reflect.setPrototypeOf
js
const obj = { a: 1 };
const proto = { b: 2 };

// 获取对象的原型
console.log(Reflect.getPrototypeOf(obj)); // {}

// 设置对象的原型
Reflect.setPrototypeOf(obj, proto);
console.log(Reflect.getPrototypeOf(obj)); // { b: 2 }

1.3. reflect-metadata

reflect-metadata 是一个用于 TypeScript 和 ECMAScript 提案的元数据反射库。它通过提供对元数据的定义和检索支持,简化了装饰器(Decorators)的使用。该库实现了多种元数据相关功能,可以在类、方法、参数和属性上设置和获取元数据。

1.3.1 安装

要使用 reflect-metadata,首先需要将其安装到你的项目中。你可以使用 npm 或 yarn 进行安装:

js
npm install reflect-metadata

或者使用 yarn:

js
yarn add reflect-metadata

1.3.2 使用方法

在使用 reflect-metadata 之前,需要在代码的入口文件(例如 index.ts 或 main.ts)中引入 reflect-metadata:

js
import 'reflect-metadata';

1.3.3 主要功能

reflect-metadata 提供了一组用于定义和检索元数据的方法:

  • Reflect.defineMetadata(metadataKey, metadataValue, target, propertyKey):定义元数据。
  • Reflect.hasMetadata(metadataKey, target, propertyKey):检查目标对象是否具有指定的元数据。
  • Reflect.getMetadata(metadataKey, target, propertyKey):获取目标对象的元数据。
  • Reflect.getOwnMetadata(metadataKey, target, propertyKey):获取目标对象的自有元数据。
  • Reflect.deleteMetadata(metadataKey, target, propertyKey):删除目标对象的元数据。
js
import 'reflect-metadata';
// 定义一个类
class MyClass {
  private myProperty: string;

  constructor(value: string) {
    this.myProperty = value;
  }

  // 定义一个方法,并为其添加元数据
  @Reflect.metadata('customKey', 'customValue')
  myMethod() {
    console.log(`Executing myMethod`);
  }
}

// 实例化 MyClass
const instance = new MyClass('Hello');

// 1. 定义元数据
Reflect.defineMetadata('key1', 'value1', instance, 'myProperty');

// 2. 检查是否具有指定的元数据
const hasMetadata = Reflect.hasMetadata('key1', instance, 'myProperty');
console.log(`Has metadata 'key1' for 'myProperty': ${hasMetadata}`);

// 3. 获取元数据
const metadataValue = Reflect.getMetadata('key1', instance, 'myProperty');
console.log(`Metadata 'key1' value for 'myProperty': ${metadataValue}`);

// 4. 获取自有元数据(针对方法)
const ownMetadataValue = Reflect.getOwnMetadata('customKey', instance, 'myMethod');
console.log(`Own metadata 'customKey' value for 'myMethod': ${ownMetadataValue}`);

// 5. 删除元数据
Reflect.deleteMetadata('key1', instance, 'myProperty');
const deletedMetadata = Reflect.getMetadata('key1', instance, 'myProperty');
console.log(`Metadata 'key1' after deletion: ${deletedMetadata}`);

2.类

2.1 类的定义

TypeScript 中的类是基于 ECMAScript 2015(ES6)标准的实现,并在其基础上增加了类型支持和其他特性,使得面向对象编程更加完善和强大。以下是 TypeScript 中类的详细讲解。

基本概念

  1. 类的定义:类是对象的蓝图,定义了对象的属性和方法。
  2. 构造函数:constructor 是一个特殊的方法,用于在创建对象实例时初始化对象。
  3. 成员变量:类的属性,可以是实例属性或静态属性。
  4. 方法:类的行为,可以是实例方法或静态方法。
js
class Person {
  // 实例属性
  name: string;
  age: number;
  // 静态属性
  static species: string = 'Homo sapiens';
  // 构造函数
  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
  // 实例方法
  greet(): string {
    return `Hello, my name is ${this.name} and I am ${this.age} years old.`;
  }
  // 静态方法
  static speciesInfo(): string {
    return `All humans belong to the species ${Person.species}.`;
  }
}
// 创建类的实例
const person1 = new Person('John', 30);
// 调用实例方法
console.log(person1.greet()); // 输出: Hello, my name is John and I am 30 years old.
// 调用静态方法
console.log(Person.speciesInfo()); // 输出: All humans belong to the species Homo sapiens.
js
// 构造函数
function Person(name, age) {
  this.name = name;
  this.age = age;
}
// 实例方法
Person.prototype.greet = function () {
  return 'Hello, my name is ' + this.name + ' and I am ' + this.age + ' years old.';
};
// 静态属性
Person.species = 'Homo sapiens';
// 静态方法
Person.speciesInfo = function () {
  return 'All humans belong to the species ' + Person.species + '.';
};
// 创建类的实例
var person1 = new Person('John', 30);
// 调用实例方法
console.log(person1.greet()); // 输出: Hello, my name is John and I am 30 years old.
// 调用静态方法
console.log(Person.speciesInfo()); // 输出: All humans belong to the species Homo sapiens.

2.2 装饰器

在 TypeScript 中,装饰器是一种特殊类型的声明,它能够附加到类声明、方法、访问符、属性或参数上,可以修改类的行为。装饰器是一个实验性的特性,需要在 tsconfig.json 文件中启用 experimentalDecorators 编译器选项。

2.2.1 装饰器的类型

  1. 类装饰器(Class Decorators):应用于类构造函数,可以用于修改类的定义。

  2. 方法装饰器(Method Decorators):应用于方法,可以用于修改方法的行为。

  3. 访问器装饰器(Accessor Decorators):应用于类的访问器属性(getter 或 setter)。

  4. 属性装饰器(Property Decorators):应用于类的属性。

  5. 参数装饰器(Parameter Decorators):应用于方法参数。

装饰器名称装饰器描述装饰器的参数说明
类装饰器 (Class Decorators)应用于类构造函数,可以用于修改类的定义。constructor: Function
方法装饰器 (Method Decorators)应用于方法,可以用于修改方法的行为。target: Object, propertyKey: string, descriptor: PropertyDescriptor
访问器装饰器 (Accessor Decorators)应用于类的访问器属性(getter 或 setter)。target: Object, propertyKey: string, descriptor: PropertyDescriptor
属性装饰器 (Property Decorators)应用于类的属性。target: Object, propertyKey: string
参数装饰器 (Parameter Decorators)应用于方法参数。target: Object, propertyKey: string, parameterIndex: number

2.2.2 类装饰器

2.2.2.1 简单类装饰器
js
function logClass(constructor: Function) {
    console.log("Class created:", constructor.name);
}

@logClass
class Person {
    constructor(public name: string) {}
}

// 输出: Class created: Person
2.2.2.2 类装饰器工厂

装饰器工厂是一个返回装饰器函数的函数,可以接受参数来控制装饰器的行为。

js
function logClassWithParams(message: string) {
    return function (constructor: Function) {
        console.log(message, constructor.name);
    };
}

@logClassWithParams("Creating class:")
class Car {
    constructor(public model: string) {}
}

// 输出: Creating class: Car
2.2.2.3 修改类的行为

这个装饰器扩展了类的功能,添加了一个新的属性和方法。

js
function addTimestamp<T extends { new(...args: any[]): {} }>(constructor: T) {
    return class extends constructor {
        timestamp = new Date();
    };
}
interface Document{
    timestamp: Date;
}
@addTimestamp
class Document {
    constructor(public title: string) {}
}

const doc = new Document("My Document");
//const doc = new Document("My Document") as Document & { timestamp: Date };
console.log(doc.title); // My Document
console.log(doc.timestamp); // 当前日期和时间
export {}
2.2.2.4 替换类的构造函数

可以通过返回一个新的构造函数来替换原有的构造函数,从而修改类的实例化过程。

js
function replaceConstructor<T extends { new(...args: any[]): {} }>(constructor: T) {
    return class extends constructor {
        constructor(...args: any[]) {
            super(...args);
            console.log("Instance created");
        }
    };
}

@replaceConstructor
class User {
    constructor(public name: string) {}
}

const user = new User("Alice");
// 输出: Instance created

2.2.3 方法装饰器

在 TypeScript 中,方法装饰器(Method Decorators)用于修饰类的方法。它们可以用于修改方法的行为、添加元数据、进行日志记录、权限检查等。方法装饰器的目标是类的方法,其签名为

js
(target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor) => void | PropertyDescriptor。
2.2.3.1 方法装饰器的语法

方法装饰器是一个接受三个参数的函数:

  1. target:装饰的目标对象,对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
  2. propertyKey:装饰的成员名称。
  3. descriptor:成员的属性描述符。
2.2.3.2 日志记录

可以在方法调用前后记录日志,跟踪方法调用情况。

js
function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value;
    descriptor.value = function (...args: any[]) {
        console.log(`Calling ${propertyKey} with arguments: ${args}`);
        const result = originalMethod.apply(this, args);
        console.log(`Result: ${result}`);
        return result;
    };
    return descriptor;
}
class Calculator {
    @log
    add(a: number, b: number): number {
        return a + b;
    }
}

const calc = new Calculator();
calc.add(2, 3);
2.2.3.3 权限检查

可以在方法调用前检查用户权限,决定是否允许调用。

js
function authorize(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value;
    descriptor.value = function (...args: any[]) {
        const user = { roles: ['admin'] };
        if (!user.roles.includes('admin')) {
            throw new Error("User is not authorized to call this method");
        }
        return originalMethod.apply(this, args);
    };
    return descriptor;
}
class AdminPanel {
    @authorize
    deleteUser(userId: string) {
        console.log(`User ${userId} deleted`);
    }
}
const adminPanel = new AdminPanel();
adminPanel.deleteUser('123'); // User 123 deleted
2.2.3.4 方法缓存

可以缓存方法的返回结果,以提高性能。

js
function cache(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value;
    const cacheMap = new Map<string, any>();
    descriptor.value = function (...args: any[]) {
        const key = JSON.stringify(args);
        if (cacheMap.has(key)) {
            return cacheMap.get(key);
        }
        const result = originalMethod.apply(this, args);
        cacheMap.set(key, result);
        return result;
    };
    return descriptor;
}
class MathOperations {
    @cache
    factorial(n: number): number {
        if (n <= 1) {
            return 1;
        }
        return n * this.factorial(n - 1);
    }
}
const mathOps = new MathOperations();
console.log(mathOps.factorial(5)); // 120
console.log(mathOps.factorial(5)); // 从缓存中获取结果

2.2.4 访问器装饰器

访问器装饰器(Accessor Decorators)是 TypeScript 中的一种装饰器类型,用于装饰类的访问器属性(getter 和 setter)。访问器装饰器可以用于修改或替换访问器的行为,添加元数据,进行日志记录等。

2.2.4.1 访问器装饰器的语法

访问器装饰器是一个接受三个参数的函数:

target:装饰的目标对象,对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。 propertyKey:访问器的名称。 descriptor:访问器的属性描述符。 访问器装饰器的签名为 (target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor) => void | PropertyDescriptor。

2.2.4.2 日志记录

可以在访问器的 get 和 set 方法中添加日志记录,以跟踪属性的访问和修改。

js
function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const originalGet = descriptor.get;
    const originalSet = descriptor.set;
    if (originalGet) {
        descriptor.get = function() {
            const result = originalGet.apply(this);
            console.log(`Getting value of ${propertyKey}: ${result}`);
            return result;
        };
    }
    if (originalSet) {
        descriptor.set = function(value: any) {
            console.log(`Setting value of ${propertyKey} to: ${value}`);
            originalSet.apply(this, [value]);
        };
    }
    return descriptor;
}
class User {
    private _name: string;
    constructor(name: string) {
        this._name = name;
    }
    @log
    get name() {
        return this._name;
    }
    set name(value: string) {
        this._name = value;
    }
}
const user = new User("Alice");
console.log(user.name); // Getting value of name: Alice
user.name = "Bob"; // Setting value of name to: Bob
console.log(user.name); // Getting value of name: Bob
2.2.4.3 权限控制

可以在访问器中添加权限检查,以控制属性的访问权限。

js
function adminOnly(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const originalGet = descriptor.get;
    descriptor.get = function() {
        const user = { role: 'user' }; // 示例用户对象
        if (user.role !== 'admin') {
            throw new Error("Access denied");
        }
        return originalGet.apply(this);
    };
    return descriptor;
}
class SecureData {
    private _secret: string = "top secret";
    @adminOnly
    get secret() {
        return this._secret;
    }
}
const data = new SecureData();
try {
    console.log(data.secret); // 抛出错误: Access denied
} catch (error) {
    console.log(error.message);
}

2.2.5 属性装饰器

属性装饰器(Property Decorators)是 TypeScript 提供的一种特殊装饰器,用于修饰类的属性。属性装饰器用于添加元数据或进行属性初始化等操作,但不同于方法装饰器和类装饰器,它不能直接修改属性的值或属性描述符。

2.2.5.1 属性装饰器的语法

属性装饰器是一个接受两个参数的函数:

  1. target:装饰的目标对象,对于静态属性来说是类的构造函数,对于实例属性是类的原型对象。
  2. propertyKey:装饰的属性名称。 属性装饰器的签名为 (target: Object, propertyKey: string | symbol) => void。
2.2.5.2 元数据添加

属性装饰器常用于添加元数据,可以结合 Reflect API 使用,以便在运行时获取元数据。

js
import "reflect-metadata";
function required(target: any, propertyKey: string) {
    Reflect.defineMetadata("required", true, target, propertyKey);
}
class User {
    @required
    username: string;
}
function validate(user: User) {
    for (let key in user) {
        if (Reflect.getMetadata("required", user, key) && !user[key]) {
            throw new Error(`Property ${key} is required`);
        }
    }
}
const user = new User();
user.username = "";
validate(user); // 抛出错误:Property username is required
2.2.5.3 属性访问控制
  • 可以使用属性装饰器来定义属性的访问控制或初始值设置。
js
function defaultValue(value: string) {
  return function (target: any, propKey: string) {
    let val = value;
    const getter = function () {
      return val;
    };
    const setter = function (newVal) {
      val = newVal;
    };
    Object.defineProperty(target, propKey, {
      enumerable: true,
      configurable: true,
      get: getter,
      set: setter,
    });
  };
}

class Settings {
  @defaultValue("dark")
  theme: string;
}

const s1 = new Settings();
console.log(Object.getOwnPropertyDescriptor(Object.getPrototypeOf(s1), "theme"));//有值
console.log(Object.getOwnPropertyDescriptor(s1, "theme"));//undefined
console.log(s1.theme, "--theme");//dark --theme
  • 请注意上述写法仅限于target为ES2015时可用,因为在老版本中类的属性是放在原型对象上的
js
{
    "compilerOptions": {
    "target": "ES2015"
    }
}
  • 对于版本无效,因为在新版本中,类的属性是放在类的实例上的
js
{
    "compilerOptions": {
    "target": "ESNext",
    }
}
  • 如果是新版本的话就需要使用类装饰器了
js
function defaultValues(defaults: { [key: string]: any }) {
  return function <T extends { new(...args: any[]): {} }>(constructor: T) {
    return class extends constructor {
      constructor(...args: any[]) {
        super(...args);
        Object.keys(defaults).forEach(key => {
          if (this[key] === undefined) {
            this[key] = defaults[key];
          }
        });
      }
    }
  }
}
@defaultValues({
  theme: "dark",
})
class Settings {
  theme: string;
}
const s1 = new Settings();
console.log(s1.theme);  // 输出应该是 "dark"
2.2.5.4 注意事项

不可直接修改属性值: 属性装饰器不能直接修改属性值或描述符,只能用于添加元数据或做一些初始化操作。

配合其他装饰器使用: 属性装饰器通常与其他类型的装饰器(如方法装饰器、类装饰器)配合使用,以实现更复杂的功能。

2.2.6 参数装饰器

参数装饰器(Parameter Decorators)是 TypeScript 中的一种装饰器类型,用于修饰类构造函数或方法的参数。参数装饰器主要用于为参数添加元数据,以便在运行时能够获取这些元数据并进行相应的处理。与其他装饰器不同,参数装饰器不直接修改参数的行为或值。

2.2.6.1 参数装饰器的语法

参数装饰器是一个接受三个参数的函数:

  1. target:装饰的目标对象,对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
  2. propertyKey:参数所属的方法的名称。
  3. parameterIndex:参数在参数列表中的索引。 参数装饰器的签名为 (target: Object, propertyKey: string | symbol, parameterIndex: number) => void。
2.2.6.2 参数验证

可以使用参数装饰器在方法调用时验证参数的值。

js
// 引入 reflect-metadata 库,用于反射元数据操作
import "reflect-metadata";
// 参数装饰器函数,用于验证方法参数
function validate(target: any, propertyKey: string, parameterIndex: number) {
    // 获取现有的必需参数索引数组,如果不存在则初始化为空数组
    const existingRequiredParameters: number[] = Reflect.getOwnMetadata("requiredParameters", target, propertyKey) || [];
    // 将当前参数的索引添加到必需参数索引数组中
    existingRequiredParameters.push(parameterIndex);
    // 将更新后的必需参数索引数组存储到方法的元数据中
    Reflect.defineMetadata("requiredParameters", existingRequiredParameters, target, propertyKey);
}
// 方法装饰器函数,用于在方法调用时验证必需参数
function validateParameters(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    // 保存原始方法
    const method = descriptor.value;
    // 修改方法,使其在调用时验证必需参数
    descriptor.value = function (...args: any[]) {
        // 获取方法的必需参数索引数组
        const requiredParameters: number[] = Reflect.getOwnMetadata("requiredParameters", target, propertyKey) || [];
        // 遍历必需参数索引数组,检查相应的参数是否为 undefined
        for (let parameterIndex of requiredParameters) {
            if (args[parameterIndex] === undefined) {
                // 如果必需参数为 undefined,则抛出错误
                throw new Error(`Missing required argument at position ${parameterIndex}`);
            }
        }
        // 调用原始方法并返回其结果
        return method.apply(this, args);
    };
}
// 定义 User 类
class User {
    // 构造函数,初始化 name 属性
    constructor(private name: string) {}
    // 使用 validateParameters 方法装饰器装饰 setName 方法
    @validateParameters
    setName(@validate newName: string) {
        // 设置新的 name 属性值
        this.name = newName;
    }
}
// 创建一个 User 实例
const user = new User("Alice");
// 调用 setName 方法,传入有效参数
user.setName("Bob"); // 正常
// 调用 setName 方法,传入 undefined 作为参数,触发参数验证错误
user.setName(undefined); // 抛出错误: Missing required argument at position 0
// 导出一个空对象,以避免模块级别作用域污染
export {}
2.2.6.4 注意事项

只能用于参数: 参数装饰器只能应用于方法的参数,不能应用于类或属性。

依赖反射元数据: 参数装饰器通常依赖 Reflect API 来存储和访问元数据,因此需要引入 reflect-metadata 库,并在 tsconfig.json 中启用 emitDecoratorMetadata 选项。

2.2.7 各种装饰器的执行顺序

执行顺序

  1. 属性装饰器(Property Decorators)和方法装饰器(Method Decorators)以及访问器装饰器(Accessor Decorators)
  • 按照它们在类中出现的顺序,从上到下依次执行。
  1. 参数装饰器(Parameter Decorators)
  • 在执行方法装饰器之前执行,按照参数的位置从右到左依次执行。
  • 对于同一个参数的多个装饰器,也是从从右向左依次执行
  1. 类装饰器(Class Decorators)
  • 最后执行。

示例代码及执行顺序

以下是一个示例代码,展示了各种装饰器的执行顺序:

js
function classDecorator() {
    return function (constructor: Function) {
        console.log('Class decorator');
    };
}

function methodDecorator() {
    return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
        console.log('Method decorator');
    };
}

function accessorDecorator() {
    return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
        console.log('Accessor decorator');
    };
}

function propertyDecorator() {
    return function (target: any, propertyKey: string) {
        console.log('Property decorator');
    };
}

function parameterDecorator() {
    return function (target: any, propertyKey: string, parameterIndex: number) {
        console.log('Parameter decorator');
    };
}

@classDecorator()
class Example {
    @propertyDecorator()
    prop: string;

    @accessorDecorator()
    get myProp() {
        return this.prop;
    }

    @methodDecorator()
    method(@parameterDecorator() param: any) {
        console.log('Method execution');
    }
}

执行顺序的输出

js
Property decorator
Accessor decorator
Parameter decorator
Method decorator
Class decorator
js
function parameter1Decorator1() {
    return function (target: any, propertyKey: string, parameterIndex: number) {
        console.log('parameter1Decorator1');
    };
}
function parameter1Decorator2() {
    return function (target: any, propertyKey: string, parameterIndex: number) {
        console.log('parameter1Decorator2');
    };
}
function parameter2Decorator1() {
    return function (target: any, propertyKey: string, parameterIndex: number) {
        console.log('parameter2Decorator1');
    };
}
function parameter2Decorator2() {
    return function (target: any, propertyKey: string, parameterIndex: number) {
        console.log('parameter2Decorator2');
    };
}

class Example {
    method(
        @parameter1Decorator1() @parameter1Decorator2()param1,
        @parameter2Decorator1() @parameter2Decorator2()param2
    ) {
        console.log('Method execution');
    }
}

执行顺序的输出

js
parameter2Decorator2
parameter2Decorator1
parameter1Decorator2
parameter1Decorator1