クラスメソッド内のTypescript「this」


84

これはおそらく痛々しいほど基本的なことだと思いますが、頭を包むのに苦労しています。

class Main
{
     constructor()
     {
         requestAnimationFrame(this.update);  //fine    
     }

     update(): void
     {
         requestAnimationFrame(this.update);  //error, because this is window
     }

}

プロキシが必要なようですので、Jqueryを使用するとしましょう

class Main
{
     constructor()
     {
         this.updateProxy = $.proxy(this.update, this);
         requestAnimationFrame(this.updateProxy);  //fine    
     }

     updateProxy: () => void
     update(): void
     {
         requestAnimationFrame(this.updateProxy);  //fine
     }

}

しかし、Actionscript 3のバックグラウンドから来ているので、ここで何が起こっているのかよくわかりません。申し訳ありませんが、Javascriptがどこから始まり、TypeScriptがどこで終わるのかわかりません。

updateProxy: () => void

また、私はこれを正しく行っているとは確信していません。私が最後に欲しいのはaProxy()、同じことを2回書いていると感じるので、アクセスする必要があるaa()関数を持つクラスのほとんどです。正常ですか?


私は、このドキュメントが非常に役に立ったgithub.com/Microsoft/TypeScript/wiki/...
rainversion_3

回答:


119

thisTypeScriptをキャプチャする場合は、矢印関数を使用します。アンダースを引用するには:

this矢印の機能ではレキシカルスコープされます

これが私の利点にこれを使用するのが好きな方法です:

class test{
    // Use arrow functions
    func1=(arg:string)=>{
            return arg+" yeah" + this.prop;
    }
    func2=(arg:number)=>{
            return arg+10 + this.prop;
    }       

    // some property on this
    prop = 10;      
}

TypeScriptプレイグラウンドでこれを表示します

生成されたJavaScriptthisは、関数呼び出しの外部でキャプチャされていることがわかります。

var _this = this;
this.prop = 10;
this.func1 = function (arg) {
    return arg + " yeah" + _this.prop;
};

そのため、this関数呼び出し内の値(可能性がありますwindow)は使用されません。

詳細:thisTypeScriptの理解」(4:05)– YouTube


2
これは必要ありません。あなたが提案しているのは慣用的なJavaScriptですが、TypeScriptはこれを不要にします。
Tatiana Racheva 2013

1
クラスメンバーのコンテキストで矢印関数を使用する@TatianaRachevaは、TS 0.9.1より前では許可されていませんでした(この回答はその前でした)。新しい構文に対する回答を更新しました:)
basarat 2013

19
あなたはビデオを見る必要があります-非常に便利です。
わずか

1
ありがとう、@ basarat。ビデオの途中で矢印機能を使用しているのを見るとすぐに、電球がこのコンテキストのコンテキストになりました。あなたに感謝する。
サイモン

1
TypeScriptプレイグラウンドの@AaronLSを生成var _this = this;するには、選択する必要がありますES5。内部の機能- - ES2015以来thisの代わりに使用される_this
ステファノSpinucci

19

このようにメソッドを作成すると、「this」は期待どおりに扱われます。

class Main
{
    constructor()
    {
        requestAnimationFrame(() => this.update());
    }

    update(): void
    {
        requestAnimationFrame(() => this.update());
    }
}

別のオプションは、「this」を関数呼び出しにバインドすることです。

class Main
{
    constructor()
    {
        requestAnimationFrame(this.update.bind(this));
    }

    update(): void
    {
        requestAnimationFrame(this.update.bind(this));
    }
}

2
私の経験では、更新関数は次のように定義されています。update=()=> {...}
Shaun Rowan

私はtypescriptを頻繁に使用していて、何度も行ったり来たりしています。現時点では、単純なメソッド呼び出し以上のものである場合にのみ中括弧を含めます。また、typescript + linq(これは敬虔です)を使用する場合、形式はより優れています。例:Enumerable.From(arr).Where(o => o.id == 123);
joelnet 2014

生成されたJavaScriptを見ると、大きな違いがあると思います。それは好みの問題ではありません。update =()=> {}は、「var _this = this」コンパイルを介して字句スコープを作成しますが、構文は作成しません。
ショーンローワン2014

1
TypeScriptライブラリには「_this」コンテキストが絶対に含まれているため、アップグレードが必要になる場合があります。"()=> code()"または()=> {return code();を使用します。} "は100%同一のJavaScriptコードを出力します。出力は次のとおりです:i.imgur.com/I5J12GE.png。コードをtypescriptlang.org/Playground
joelnet 2014

どうやらbind(this)は、元の関数argsの型安全性を失うため、悪い可能性があります
robert king

6

タイプスクリプト言語仕様の72ページを参照してください https://github.com/Microsoft/TypeScript/blob/master/doc/TypeScript%20Language%20Specification.pdf?raw=true

矢印関数式

例では

class Messenger {
 message = "Hello World";
 start() {
 setTimeout(() => alert(this.message), 3000);
 }
};
var messenger = new Messenger();
messenger.start();

矢印関数式を使用すると、コールバックは周囲の「start」メソッドと同じになります。コールバックを標準の関数式として作成するには、たとえばローカル変数にコピーするなどして、これを囲むアクセスを手動で調整する必要があります。

これは実際に生成されたJavascriptです。

class Messenger {
 message = "Hello World";
 start() {
 var _this = this;
 setTimeout(function() { alert(_this.message); }, 3000);
 }
};

リンクは、残念ながら壊れている
ジミー・ケイン

1
@JimmyKaneリンクを更新しました。不思議なことに、このドキュメントは1年以上前のものであり、まだホームページで参照されていますが、重要なコンテンツを回答に含めました。
Simon_Weaver 2017年

4

この問題は、関数をコールバックとして渡すときに発生します。コールバックが実行されるまでに、「this」の値はウィンドウ、コールバックを呼び出すコントロール、またはその他のものに変更されている可能性があります。

コールバックする関数への参照を渡す時点で、常にラムダ式を使用するようにしてください。例えば

public addFile(file) {
  this.files.push(file);
}
//Not like this
someObject.doSomething(addFile);
//but instead, like this
someObject.doSomething( (file) => addFile(file) );

これは次のようにコンパイルされます

this.addFile(file) {
  this.files.push(file);
}
var _this = this;
someObject.doSomething(_this.addFile);

addFile関数は特定のオブジェクト参照(_this)で呼び出されているため、呼び出し元の「this」ではなく、_thisの値を使用します。


それが何にコンパイルされるかを言うとき、あなたはどちらを見せていますか?(矢印インスタンスまたはメソッドオブジェクトを渡すだけのインスタンス?)
Vaccano 2015年

ラムダ。そのコードでTSを作成し、それが何をコンパイルするかを見てください。
ピーターモリス

3

パーティーに非常に遅れていますが、この質問の将来の訪問者にとって、次のことを考慮することが非常に重要だと思います。

受け入れられたものを含む他の答えは、重要な点を見逃しています:

myFunction() { ... }

そして

myFunction = () => { ... }

「後者がキャプチャすることを除いて」同じものではありませんthis

最初の構文はプロトタイプにメソッドを作成し、2番目の構文は値が関数であるオブジェクトにプロパティを作成します(これはキャプチャにも発生しますthis)。これは、トランスパイルされたJavaScriptではっきりと確認できます。

完了するには:

myFunction = function() { ... }

2番目の構文と同じですが、キャプチャはありません。

したがって、ほとんどの場合、矢印構文を使用すると、オブジェクトへのバインドの問題が修正されますが、それは同じではなく、プロパティの代わりにプロトタイプで適切な関数を使用したい場合が多くあります。

これらの場合、プロキシを使用するか、.bind()実際に正解。(読みやすさに苦しんでいますが。)

ここでもっと読む(主にTypeScriptについてではなく、原則が成り立つ):

https://medium.com/@charpeni/arrow-functions-in-class-properties-might-not-be-as-great-as-we-think-3b3551c440b1

https://ponyfoo.com/articles/binding-methods-to-class-instance-objects


2

つまり、thisキーワードには、関数を呼び出したオブジェクトへの参照が常に含まれています。

Javascriptでは、関数は単なる変数であるため、関数を渡すことができます。

例:

var x = {
   localvar: 5, 
   test: function(){
      alert(this.localvar);
   }
};

x.test() // outputs 5

var y;
y.somemethod = x.test; // assign the function test from x to the 'property' somemethod on y
y.test();              // outputs undefined, this now points to y and y has no localvar

y.localvar = "super dooper string";
y.test();              // outputs super dooper string

jQueryで次のことを行う場合:

$.proxy(this.update, this);

あなたがしていることは、そのコンテキストをオーバーライドすることです。舞台裏では、jQueryはこれを教えてくれます:

$.proxy = function(fnc, scope){
  return function(){
     return fnc.apply(scope);  // apply is a method on a function that calls that function with a given this value
  }
};

1

このようにしてみませんか?タイプ「myClass」のグローバル変数を宣言し、クラスのコンストラクターで初期化します。

var _self: myClass;

class myClass {
    classScopeVar: string = "hello";

    constructor() {
        _self = this;
    }

    alerter() {
        setTimeout(function () {
            alert(_self.classScopeVar)
        }, 500);
    }
}

var classInstance = new myClass();
classInstance.alerter();

注:すでに特別な意味として「自己」を使用しないことが重要です。


大きな問題:クラスのすべてのインスタンスが同じ_selfを持っているため、機能しません。
アストラベア
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.