我有一个打字稿装饰工厂,控制台记录执行函数所花费的总时间,实际函数执行结果以及传递给装饰器的参数。
e.g。
export function performaceLog(...args: any[]) {
return (target: Object, key: string, descriptor: TypedPropertyDescriptor<any>) => {
var msg = '';
if (args.length != 0) {
msg = args.map(arg => arg).join(' ');
}
if (descriptor === undefined) {
descriptor = Object.getOwnPropertyDescriptor(target, key);
}
if (typeof descriptor.value !== 'function') {
throw new SyntaxError('Only functions can be used with log decorators');
}
var originalMethod = descriptor.value.bind(target);
descriptor.value = function() {
var funcArgs: any = [];
for (var i = 0; i < arguments.length; i++) {
funcArgs[i - 0] = arguments[i];
}
var startTime = performance.now();
console.log(`Begin function ${key} with params (${funcArgs}) : ${msg}`);
var result = originalMethod.apply(this, funcArgs);
var endTime = performance.now();
console.log(`End function ${key}. Execution time: ${endTime - startTime} milliseconds. Return result : ${result}`);
return result;
};
return descriptor;
};
}
我正在使用上面的装饰器,其中包含一个类中的函数: (考虑到我的班级还有其他方法,如ctor,get,set和utils)。
class LoggerService {
@performaceLog('validate', 'message')
handleMessage(message) {
console.log(message);
return true;
};
}
函数调用如下所示:
handleMessage('decoratorValidation');
这给了我完美的输出:
Begin function handleMessage with params (decoratorValidation) : validate message
decoratorValidation
End function handleMessage. Execution time: 0.3000000142492354 milliseconds.
Return result : true
但是当我将函数handleMessage更改为支持箭头格式(ES6)时,它会抛出一个错误:
@performaceLog('validate', 'message')
handleMessage = message => {
console.log(message);
return true;
};
错误讯息:
Unable to resolve signature of property decorator when called as an expression.
我在tsconfig.json中正确设置了所有参数。我的整个项目都支持&#34; ES6&#34;目标,我希望装饰器支持箭头功能。
答案 0 :(得分:4)
performaceLog
应该只使用原型方法,因为它依赖descriptor
,这应该是可选的。
descriptor
类字段没有handleMessage = message => ...
,因为它在类原型上不存在。类字段仅在构造函数中分配给this
。
此行不会出于同样的原因:
descriptor = Object.getOwnPropertyDescriptor(target, key);
为了在装饰器中修补箭头方法,应该在类原型上设置陷阱。以下是通用装饰器的示例,可以与原型和实例方法一起使用;它使用get
/ set
来捕获正确的this
上下文并将装饰函数缓存到patchFn
变量。无论descriptor
参数如何,它都会返回描述符:
function universalMethodDecorator(target: any, prop: string, descriptor?: TypedPropertyDescriptor<any>): any {
let fn;
let patchedFn;
if (descriptor) {
fn = descriptor.value;
}
return {
configurable: true,
enumerable: false,
get() {
if (!patchedFn) {
patchedFn = (...args) => fn.call(this, ...args);
}
return patchedFn;
},
set(newFn) {
patchedFn = undefined;
fn = newFn;
}
};
}
这仅适用于TypeScript装饰器。 Babel legacy decorators可能表现不同。
如this answer中所述,由于多种原因,原型方法可能比实例方法更受欢迎。其中一个原因是它们可以无缝装饰,因为装饰器应用于类原型。 arrow方法的唯一真正好处是它自然地绑定到类实例,但由于装饰器已经在使用中,如果需要,可以在装饰器中绑定原型方法(这是universalMethodDecorator
基本上所做的;它是noop对于箭头方法)。