面试题答案
一键面试装饰器原理
装饰器是一种特殊类型的声明,它能够被附加到类声明、方法、访问器、属性或参数上。它本质上是一个函数,在运行时对目标进行额外的操作,比如修改目标的行为、添加新的功能等。在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());
- 外层函数
callLogger
:接受threshold
(阈值)和presetValue
(预设值)作为参数,返回一个装饰器函数。 - 装饰器函数:接受
target
(目标对象,对于实例方法是类的原型对象)、propertyKey
(方法名)和descriptor
(方法的属性描述符)作为参数。 - 定义变量:在装饰器函数内部定义
callCount
记录调用次数,callTimestamps
记录每次调用的时间戳,originalMethod
保存原始方法。 - 修改
descriptor.value
:将原始方法替换为新的函数,新函数在每次调用时增加调用次数、记录时间戳,并在调用次数达到阈值时返回预设值,否则返回原始方法的执行结果。 - 添加属性:通过
Object.defineProperty
为新函数添加callCount
和callTimestamps
属性,用于获取调用次数和时间戳数组。 - 类型系统处理:在上述代码中,
target
类型为any
,因为不同的装饰器应用场景下target
类型不同(类装饰器、方法装饰器等)。propertyKey
为string
类型,代表方法名。descriptor
类型为PropertyDescriptor
,它定义了方法的属性描述符。对于新定义的函数参数args
使用any[]
表示接受任意类型的参数数组,返回值类型根据原始方法和预设值来决定,整体代码在满足功能需求的同时也遵循了TypeScript类型系统的基本规则。