How to avoid hard coded this? in Decorators

蹲街弑〆低调 提交于 2019-11-29 03:09:54

问题


I have read "How to implement a typescript decorator?" and multiple sources but there is something that i have nor been able to do with decorators.

class FooBar {
    public foo(arg): void { 
        console.log(this);
        this.bar(arg);
    }
    private bar(arg) : void { 
        console.log(this, "bar", arg);
    }
}

If we invoke the function foo:

var foobar = new FooBar();
foobar.foo("test"); 

The object FooBar is logged in the console by console.log(this); in foo

The string "FooBar {foo: function, bar: function} bar test" is logged in the console by console.log(this, "bar", arg); in bar.

Now let's use a decorator:

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;
        }
    };
}

We use the same function but decorated:

class FooBar {
    @log
    public foo(arg): void { 
        console.log(this);
        this.bar(arg);
    }
    @log
    private bar(arg) : void { 
        console.log(this, "bar", arg);
    }
}

And we invoke foo as we did before:

var foobarFoo = new FooBar();
foobarFooBar.foo("test");

The objectWindow is logged in the console by console.log(this); in foo

And bar is never invoked by foo because this.bar(arg); causes Uncaught TypeError: this.bar is not a function.

The problem is the hardcoded this inside the log decorator:

value.value.apply(this, args);

How can I conserve the original this value?


回答1:


Don't use an arrow function. Use a function expression:

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;
        }
    };
}

That way it will use the function's this context instead of the value of this when log is called.


By the way, I would recommend editing the descriptor/value parameter and return that instead of overwriting it by returning a new descriptor. That way you keep the properties currently in the descriptor and won't overwrite what another decorator might have done to the descriptor:

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;
}

More details in this answer - See the "Bad vs Good" example under "Example - Without Arguments > Notes"




回答2:


I believe you can use

var self = this;

in order to preserve the 'this' at that specific point. Then, just use self at the later point where you would have wanted that particular this



来源:https://stackoverflow.com/questions/30329832/how-to-avoid-hard-coded-this-in-decorators

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!