MST

星途 面试题库

面试题:TypeScript 类的元编程与装饰器高级应用

使用 TypeScript 装饰器来实现一个日志记录功能,要求装饰器能够记录被装饰方法的调用时间、传入参数以及返回值。定义一个类 `Calculator`,其中包含几个简单的数学运算方法(如加法、减法),使用你编写的装饰器来装饰这些方法。同时,探讨一下在实际项目中,这种基于类的元编程(使用装饰器)在哪些场景下会带来显著的优势,以及可能会遇到的问题和解决方案。请写出完整代码及详细分析。
47.2万 热度难度
前端开发TypeScript

知识考点

AI 面试

面试题答案

一键面试

1. 实现日志记录装饰器及 Calculator

function logMethod(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value;
    descriptor.value = function(...args: any[]) {
        const startTime = new Date();
        console.log(`调用方法 ${propertyKey},传入参数:`, args);
        const result = originalMethod.apply(this, args);
        const endTime = new Date();
        console.log(`方法 ${propertyKey} 返回值:`, result);
        console.log(`方法 ${propertyKey} 调用耗时:`, endTime.getTime() - startTime.getTime(), 'ms');
        return result;
    };
    return descriptor;
}

class Calculator {
    @logMethod
    add(a: number, b: number): number {
        return a + b;
    }

    @logMethod
    subtract(a: number, b: number): number {
        return a - b;
    }
}

2. 详细分析

  • 日志记录装饰器 logMethod
    • 接受三个参数:target(被装饰的类的原型对象)、propertyKey(被装饰方法的名称)、descriptor(被装饰方法的属性描述符)。
    • 保存原始方法 originalMethod
    • 重新定义 descriptor.value,在新的函数中记录方法调用时间、传入参数,执行原始方法并记录返回值和调用耗时。
  • Calculator
    • 包含两个简单的数学运算方法 addsubtract,使用 @logMethod 装饰器对这两个方法进行装饰,使得每次调用这两个方法时,都会打印出调用时间、传入参数以及返回值等日志信息。

3. 基于类的元编程(使用装饰器)的优势

  • 代码复用:通过装饰器,可以将通用的功能(如日志记录、权限验证等)抽象出来,应用到多个不同的类方法上,避免在每个方法中重复编写相同的逻辑。
  • 增强可维护性:将特定功能的代码集中在装饰器中,当需要修改功能时,只需要修改装饰器代码,而不需要在每个使用该功能的方法中逐一修改,提高了代码的可维护性。
  • 提高代码可读性:使用装饰器可以使类的方法定义更加简洁明了,通过装饰器的名称可以直观地了解该方法具有哪些额外的功能。

4. 可能遇到的问题及解决方案

  • 性能问题:由于装饰器会在运行时对方法进行包装,可能会带来一定的性能开销。解决方案是在性能敏感的代码中,谨慎使用装饰器,或者对装饰器进行优化,减少不必要的操作。
  • 调试困难:装饰器会改变方法的执行逻辑,可能使得调试变得更加复杂。解决方案是在装饰器中添加详细的日志输出,便于在调试时追踪代码的执行流程。另外,现代的开发工具如 Visual Studio Code 对装饰器的调试支持越来越好,可以充分利用这些工具来辅助调试。
  • 兼容性问题:装饰器是 ES7 的提案,在一些较老的 JavaScript 运行环境中可能不支持。解决方案是使用 Babel 等工具进行转码,将使用装饰器的代码转换为兼容老版本环境的代码。