MST

星途 面试题库

面试题:TypeScript函数装饰器的深度应用

在TypeScript中,实现一个用于函数的装饰器,要求这个装饰器能够实现以下功能:记录函数的调用次数,每次调用时记录调用时间戳,并且能够在特定条件下(例如调用次数达到某个阈值),修改函数的返回值为一个预设的值。请详细解释装饰器的原理以及实现过程中对类型系统的处理。
10.9万 热度难度
前端开发TypeScript

知识考点

AI 面试

面试题答案

一键面试

装饰器原理

装饰器是一种特殊类型的声明,它能够被附加到类声明、方法、访问器、属性或参数上。它本质上是一个函数,在运行时对目标进行额外的操作,比如修改目标的行为、添加新的功能等。在TypeScript中,装饰器可以通过@符号来应用。

实现过程与类型系统处理

function callLogger(threshold: number, presetValue: any) {
    return function(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
        let callCount = 0;
        let callTimestamps: number[] = [];
        const originalMethod = descriptor.value;

        descriptor.value = function(...args: any[]) {
            callCount++;
            const timestamp = Date.now();
            callTimestamps.push(timestamp);
            if (callCount >= threshold) {
                return presetValue;
            }
            return originalMethod.apply(this, args);
        };

        Object.defineProperty(descriptor.value, 'callCount', {
            value: () => callCount,
            writable: false,
            enumerable: false,
            configurable: true
        });

        Object.defineProperty(descriptor.value, 'callTimestamps', {
            value: () => callTimestamps,
            writable: false,
            enumerable: false,
            configurable: true
        });

        return descriptor;
    };
}

class MyClass {
    @callLogger(3, '预设值')
    myMethod() {
        return '原始返回值';
    }
}

const instance = new MyClass();
console.log(instance.myMethod());
console.log(instance.myMethod());
console.log(instance.myMethod());
console.log(instance.myMethod.callCount());
console.log(instance.myMethod.callTimestamps());
  1. 外层函数callLogger:接受threshold(阈值)和presetValue(预设值)作为参数,返回一个装饰器函数。
  2. 装饰器函数:接受target(目标对象,对于实例方法是类的原型对象)、propertyKey(方法名)和descriptor(方法的属性描述符)作为参数。
  3. 定义变量:在装饰器函数内部定义callCount记录调用次数,callTimestamps记录每次调用的时间戳,originalMethod保存原始方法。
  4. 修改descriptor.value:将原始方法替换为新的函数,新函数在每次调用时增加调用次数、记录时间戳,并在调用次数达到阈值时返回预设值,否则返回原始方法的执行结果。
  5. 添加属性:通过Object.defineProperty为新函数添加callCountcallTimestamps属性,用于获取调用次数和时间戳数组。
  6. 类型系统处理:在上述代码中,target类型为any,因为不同的装饰器应用场景下target类型不同(类装饰器、方法装饰器等)。propertyKeystring类型,代表方法名。descriptor类型为PropertyDescriptor,它定义了方法的属性描述符。对于新定义的函数参数args使用any[]表示接受任意类型的参数数组,返回值类型根据原始方法和预设值来决定,整体代码在满足功能需求的同时也遵循了TypeScript类型系统的基本规则。