Как избежать жесткого кодирования этого? в декораторах
Я прочитал Как реализовать декоратор typescript? и несколько источников, но есть что-то, что я и не мог сделать с декораторами.
class FooBar {
public foo(arg): void {
console.log(this);
this.bar(arg);
}
private bar(arg) : void {
console.log(this, "bar", arg);
}
}
Если мы вызываем функцию foo
:
var foobar = new FooBar();
foobar.foo("test");
Объект FooBar
записывается в консоль на console.log(this);
в foo
Строка "FooBar {foo: function, bar: function} bar test"
записывается в консоль на console.log(this, "bar", arg);
в bar
.
Теперь позвольте использовать декоратор:
function log(target: Function, key: string, value: any) {
return {
value: (...args: any[]) => {
var a = args.map(a => JSON.stringify(a)).join();
var result = value.value.apply(this, args); // How to avoid hard coded this?
var r = JSON.stringify(result);
console.log(`Call: ${key}(${a}) => ${r}`);
return result;
}
};
}
Мы используем ту же функцию, но оформлены:
class FooBar {
@log
public foo(arg): void {
console.log(this);
this.bar(arg);
}
@log
private bar(arg) : void {
console.log(this, "bar", arg);
}
}
И мы вызываем foo
, как мы это делали раньше:
var foobarFoo = new FooBar();
foobarFooBar.foo("test");
Объект Window
записывается в консоль на console.log(this);
в foo
И bar
никогда не вызывается foo
, потому что this.bar(arg);
вызывает Uncaught TypeError: this.bar is not a function
.
Проблема заключается в жестко закодированном this
внутри декоратора log
:
value.value.apply(this, args);
Как сохранить исходное значение this
?
Ответы
Ответ 1
Не используйте функцию стрелки. Используйте выражение функции:
function log(target: Object, key: string, value: any) {
return {
value: function(...args: any[]) {
var a = args.map(a => JSON.stringify(a)).join();
var result = value.value.apply(this, args);
var r = JSON.stringify(result);
console.log(`Call: ${key}(${a}) => ${r}`);
return result;
}
};
}
Таким образом он будет использовать контекст функции this
вместо значения this
при вызове журнала.
Кстати, я бы рекомендовал отредактировать параметр дескриптор/значение и вернуть его вместо того, чтобы перезаписать его, вернув новый дескриптор. Таким образом, вы сохраняете свойства в настоящее время в дескрипторе и не будете перезаписывать то, что другой декоратор мог бы сделать с дескриптором:
function log(target: Object, key: string, descriptor: TypedPropertyDescriptor<any>) {
var originalMethod = descriptor.value;
descriptor.value = function(...args: any[]) {
var a = args.map(a => JSON.stringify(a)).join();
var result = originalMethod.apply(this, args);
var r = JSON.stringify(result);
console.log(`Call: ${key}(${a}) => ${r}`);
return result;
};
return descriptor;
}
Подробнее в этом ответе - См. пример "Плохой и хороший" в разделе "Пример - без аргументов > Примечания"
Ответ 2
Я считаю, что вы можете использовать
var self = this;
чтобы сохранить 'this' в этой конкретной точке. Затем просто используйте self
в более поздней точке, где вы хотели бы, чтобы этот this