ウェブサイト検索

javascript - デコレーターでこのハードコーディングを避ける方法


私は「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 は foo 内の console.log(this); によってコンソールに記録されます

文字列 "FooBar {foo: function, bar: function} bar test" は、bar 内の console.log(this, "bar", arg); によってコンソールにログ出力されます。

それではデコレーターを使いましょう。

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");

foo 内で console.log(this); によってオブジェクト Window がコンソールに記録されます

そして bar は foo によって決して呼び出されません。なぜなら this.bar(arg); が Uncaught TypeError: this.bar is not a function を引き起こすからです。

問題は、log デコレーター内のハードコーディングされた this です:

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

そうすることで、logが呼ばれたときの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;
}

この回答の詳細については、「例 - 引数なし > ノート」の下にある「悪い例 vs 良い例」を参照してください

関連記事