JavaScriptのthisキーワードについて
JavaScriptのthisについてまとめました。
JavaScriptのthisについて
JavaScriptのthisは、特別な変数ですべての関数実行コンテキストで作成されます。
thisとは、呼び出し元のオブジェクトへの参照を保持するキーワードのことです。
thisはJavaScriptエンジンによって準備されているキーワードで、グローバルコンテキストでも使用できますが、主に関数コンテキスト内で使用されます。
thisの値は静的(static)ではなく動的です。関数がどのように呼び出されたか参照先で変化します。
thisの値は実際に関数が呼び出されたときに値を割り当てられます。
オブジェクトのメソッドのthis
オブジェクトの中の関数(メソッド)で呼び出し元を指したいときに、thisを使います。下の例では、yamada.yearと記述するのではなく、this.yearと記述するということです。
例
calcAge()メソッドの中にあるthisは、レキシカルスコープをたどって呼び出し元のyamadaを指します。そのため、this.yearはyamada.yearと同じ意味になり、値は1900となります。
このように、オブジェクトのメソッドとして実行される場合は、thisは呼び出し元のオブジェクトを指します。
関数のthis
関数として実行される場合、thisは、グローバルオブジェクトを参照します。つまりthisの指し示す先を指定していない場合、thisはundefinedになります。
ただし、これは、strictモードを使っていないとでません。
上の例では、yamadaというオブジェクトを介してcalcAge()をメソッドとして呼び出しておらず、関数として直接呼び出しています。
このような場合、thisはグローバルオブジェクトを参照します。上の例ではwindowオブジェクトに設定していますが、通常は設定していない場合が多いのでthisはundefinedになります。
コールバック関数のthis
コールバック関数でthisのあるオブジェクトのメソッドを呼び出したととしても、thisはグローバルコンテキストを参照します。引数に関数を渡すことは、メソッドを他の変数に代入していることと同じです。refにはcalcAge()の参照先がコピーされて、ref()ではそのコピーの参照先を直接呼び出しているため、関数を実行していることと同じになります。オブジェクト経由でメソッドを呼び出しているわけではない点に注意しましょう。
コールバック関数の使い方は次の記事を参考にしてみてください。
this.yearをwindow.year(2080)ではなく、yamada.year(2000)にしたい場合、次のようにbind()を使います。
bind()メソッドとthis
bind()メソッドはthisの取りうる参照先を意図的に変更することができます。
上の例を利用すると、
このようにbind()メソッドの第一引数として指定したオブジェクトをthisの参照先にすることができます。
calcAgeのfunctionとは別に新たなfunction’としてメモリ空間に追加され、それのthisが固定されています。
bind()によってthisが固定された新たな関数がメモリ空間に生成されるという点はとても重要なので頭の片隅に置いておきましょう。
bind()の使い方は次の記事を参考にしてみてください。
setTimeout()のthis
setTimeoutはwindowオブジェクトのメソッドのため、正式にはwindow.setTimeout()となります。
thisは、オブジェクトが見つかるまでさかのぼります。
setTimeoutの中のthisが最初に見つけるのは、windowオブジェクトのため、thisの参照先はwindowになります。
これをclassのオブジェクトに変更するためには、bindを使います。
setTimeout()の中のコールバック関数にbind()を続けます。thisとしたのは、setTimeoutがある空間のthisの参照先はMyObjだからです。
setTimeout()の使い方は、次の記事を参考にしてみてください。
アロー関数のthis
アロー関数は、自身でthisを取得しません。
そのため、アロー関数のthisは、そのアロー関数の外側にあるレキシカルスコープのthisとなります。
次の例をみてみましょう。
アロー関数はthisをとらないため、レキシカルスコープをたどります。greet()のレキシカルスコープはグローバルスコープでありwindowオブジェクトになります。そこにthisは指定されていないのでundefinedになります。
そのため、上のsaito.greet();の結果は、「こんにちは、undefined」が返されます。
もしグローバルスコープでfirstNameが定義されていれば、そのfirstNameの値が取得されてしまうのでバグになります。
ちなみに、const saito = {}の{}はブロックスコープではありません。
これはオブジェクトの{}です。
このようなことが起こるため、オブジェクトのメソッドとして、アロー関数は使用しない方がいいでしょう。
イベントリスナーのthis
イベントリスナーのthisは、ハンドラーがつけるDOM要素になります。
上の例なら、thisはbtnになります。hello.logは関数として渡されているため、btnが最初に見つかるオブジェクトになります。
正しく動作させるためには、次の2つの方法があります。
bind()を使うか、コールバック関数でhello.logを関数(hello.log)としてではなく、メソッド(hello.log())として実行するかです。
関数として渡す場合と、メソッドとして実行する場合でthisの挙動が変わる点に注意しましょう。
関数として渡すのであれば、bind()でthisを調整する必要があります。
メソッドとして渡す場合、無名関数に入れる必要があります。
addEventListenerの使い方は、次の記事を参考にしてみてください。