JavaScript 中的装饰器(Decorators)高级应用:元数据反射与依赖注入
字数 1281 2025-12-08 04:44:58

JavaScript 中的装饰器(Decorators)高级应用:元数据反射与依赖注入

描述:装饰器是 JavaScript 中一种用于修改类、方法、属性或参数的语法,它基于装饰器提案,目前处于 Stage 3 阶段。装饰器允许开发者以声明式的方式增强或修改代码行为,在框架和库中广泛用于实现元数据反射、依赖注入、日志记录等功能。本知识点将深入讲解装饰器的高级应用,特别是如何结合元数据反射实现依赖注入。

解题过程:

  1. 装饰器基本语法与分类
    装饰器是一个函数,它接收目标对象的相关信息作为参数,并返回修改后的描述符或目标。装饰器分为:

    • 类装饰器:应用于类构造函数,可修改或替换类定义。
    • 方法装饰器:应用于类的方法,可修改方法行为(如日志、验证)。
    • 属性装饰器:应用于类的属性,常用于添加元数据。
    • 参数装饰器:应用于方法的参数,常用于依赖注入场景。

    示例:一个简单的类装饰器,用于添加版本信息:

    function addVersion(version) {
      return function (target) {
        target.prototype.version = version;
        return target;
      };
    }
    
    @addVersion("1.0.0")
    class MyClass {}
    console.log(new MyClass().version); // 输出:"1.0.0"
    
  2. 元数据反射 API(Reflect Metadata)
    元数据是附加到代码上的数据,用于描述代码的额外信息。JavaScript 通过 reflect-metadata 库支持反射元数据 API,提供:

    • Reflect.defineMetadata(metadataKey, metadataValue, target, propertyKey?):定义元数据。
    • Reflect.getMetadata(metadataKey, target, propertyKey?):获取元数据。
      元数据常用于存储类型信息、依赖关系等,是实现高级装饰器功能的基础。

    示例:为属性添加类型元数据:

    import "reflect-metadata";
    
    function Type(type) {
      return function (target, propertyKey) {
        Reflect.defineMetadata("design:type", type, target, propertyKey);
      };
    }
    
    class User {
      @Type(String)
      name = "";
    }
    const metadata = Reflect.getMetadata("design:type", User.prototype, "name");
    console.log(metadata); // 输出:String
    
  3. 依赖注入(Dependency Injection, DI)的实现
    依赖注入是一种设计模式,通过外部提供依赖对象,降低代码耦合度。结合装饰器和元数据,可构建简单的 DI 容器:

    • 使用类装饰器标记可注入的服务。
    • 使用参数装饰器在构造函数中声明依赖。
    • 通过反射元数据解析依赖类型,并自动实例化。

    实现步骤:
    a. 定义服务装饰器 @Injectable(),用于标记类为可注入服务:

    const Injectable = () => (target) => {
      // 可在此处注册服务到容器
      return target;
    };
    

    b. 定义注入装饰器 @Inject(),用于构造函数参数,声明依赖:

    function Inject() {
      return (target, propertyKey, parameterIndex) => {
        const paramTypes = Reflect.getMetadata("design:paramtypes", target);
        const dependencyType = paramTypes[parameterIndex];
        // 存储依赖类型信息,供容器解析
        Reflect.defineMetadata("inject:dependencies", dependencyType, target, parameterIndex);
      };
    }
    

    c. 创建 DI 容器,负责解析依赖并实例化类:

    class Container {
      services = new Map();
    
      register(token, service) {
        this.services.set(token, service);
      }
    
      resolve(TargetClass) {
        const paramTypes = Reflect.getMetadata("design:paramtypes", TargetClass) || [];
        const dependencies = paramTypes.map((type) => {
          if (this.services.has(type)) {
            return this.services.get(type);
          }
          // 递归解析依赖
          return this.resolve(type);
        });
        return new TargetClass(...dependencies);
      }
    }
    
  4. 完整示例:构建一个简易 DI 系统
    结合元数据装饰器,实现一个日志服务和用户服务的依赖注入:

    import "reflect-metadata";
    
    // 日志服务
    @Injectable()
    class Logger {
      log(message) {
        console.log(`[LOG]: ${message}`);
      }
    }
    
    // 用户服务,依赖 Logger
    @Injectable()
    class UserService {
      constructor(@Inject() logger) {
        this.logger = logger;
      }
    
      getUser(id) {
        this.logger.log(`Fetching user ${id}`);
        return { id, name: "Alice" };
      }
    }
    
    // 解析并运行
    const container = new Container();
    container.register(Logger, new Logger());
    const userService = container.resolve(UserService);
    userService.getUser(1); // 输出:[LOG]: Fetching user 1
    
  5. 高级应用:装饰器工厂与组合装饰器
    装饰器工厂是返回装饰器函数的函数,允许传递参数定制行为。多个装饰器可组合使用,执行顺序为从上到下(靠近目标的先执行):

    function log(message) {
      console.log(`Log setup: ${message}`);
      return function (target) {
        console.log(`Log applied: ${message}`);
      };
    }
    
    function seal(target) {
      Object.seal(target);
      return target;
    }
    
    @log("Class Decorator")
    @seal
    class Example {}
    // 输出顺序:
    // Log setup: Class Decorator
    // Log applied: Class Decorator
    
  6. 装饰器在框架中的应用
    现代框架如 Angular、Nest.js 大量使用装饰器实现依赖注入、路由定义、中间件等。理解装饰器与元数据反射的协作,有助于深入掌握这些框架的原理。

注意:装饰器目前需通过 Babel 或 TypeScript 编译使用,且浏览器原生支持有限。在实际项目中,应结合构建工具配置相应的插件(如 @babel/plugin-proposal-decorators)。

JavaScript 中的装饰器(Decorators)高级应用:元数据反射与依赖注入 描述:装饰器是 JavaScript 中一种用于修改类、方法、属性或参数的语法,它基于装饰器提案,目前处于 Stage 3 阶段。装饰器允许开发者以声明式的方式增强或修改代码行为,在框架和库中广泛用于实现元数据反射、依赖注入、日志记录等功能。本知识点将深入讲解装饰器的高级应用,特别是如何结合元数据反射实现依赖注入。 解题过程: 装饰器基本语法与分类 : 装饰器是一个函数,它接收目标对象的相关信息作为参数,并返回修改后的描述符或目标。装饰器分为: 类装饰器:应用于类构造函数,可修改或替换类定义。 方法装饰器:应用于类的方法,可修改方法行为(如日志、验证)。 属性装饰器:应用于类的属性,常用于添加元数据。 参数装饰器:应用于方法的参数,常用于依赖注入场景。 示例:一个简单的类装饰器,用于添加版本信息: 元数据反射 API(Reflect Metadata) : 元数据是附加到代码上的数据,用于描述代码的额外信息。JavaScript 通过 reflect-metadata 库支持反射元数据 API,提供: Reflect.defineMetadata(metadataKey, metadataValue, target, propertyKey?) :定义元数据。 Reflect.getMetadata(metadataKey, target, propertyKey?) :获取元数据。 元数据常用于存储类型信息、依赖关系等,是实现高级装饰器功能的基础。 示例:为属性添加类型元数据: 依赖注入(Dependency Injection, DI)的实现 : 依赖注入是一种设计模式,通过外部提供依赖对象,降低代码耦合度。结合装饰器和元数据,可构建简单的 DI 容器: 使用类装饰器标记可注入的服务。 使用参数装饰器在构造函数中声明依赖。 通过反射元数据解析依赖类型,并自动实例化。 实现步骤: a. 定义服务装饰器 @Injectable() ,用于标记类为可注入服务: b. 定义注入装饰器 @Inject() ,用于构造函数参数,声明依赖: c. 创建 DI 容器,负责解析依赖并实例化类: 完整示例:构建一个简易 DI 系统 : 结合元数据装饰器,实现一个日志服务和用户服务的依赖注入: 高级应用:装饰器工厂与组合装饰器 : 装饰器工厂是返回装饰器函数的函数,允许传递参数定制行为。多个装饰器可组合使用,执行顺序为从上到下(靠近目标的先执行): 装饰器在框架中的应用 : 现代框架如 Angular、Nest.js 大量使用装饰器实现依赖注入、路由定义、中间件等。理解装饰器与元数据反射的协作,有助于深入掌握这些框架的原理。 注意:装饰器目前需通过 Babel 或 TypeScript 编译使用,且浏览器原生支持有限。在实际项目中,应结合构建工具配置相应的插件(如 @babel/plugin-proposal-decorators )。