W.I.S. Laboratory
menu-bar

JavaScript


JavaScriptのthisが指し示す先はいつ決まるのか

JavaScriptはオブジェクト指向型言語(以降OOP言語)なので、他のOOP言語と同じく「this」という組み込み変数が用意されている。
一般的なOOP言語だと、this(selfと書く言語もある)はメンバ関数やインスタンスメソッド内でのみ書くことができ、「自身が所属するオブジェクト」を指す変数になっているので、コードを書いている時に this が何を指しているのかがハッキリしている。
しかしJavaScriptは this が何を指すのかがそのコードが実行された時に決まるので、コードを書いている段階では this がどこを指しているのかがハッキリしていない、という言語だ。

一般的なOOP言語だと、インスタンスメソッドではない場所(関数内やファイルスコープなど)に this と書くとエラーになってしまうことがほとんどだと思うが、JavaScriptでは通常の関数内でもファイルスコープでも、thisにアクセスできてしまう。
このとき、thisはグローバルオブジェクト(ブラウザならwindow、Node.jsなどならglobal)を指す。("use strict"していない時。"use strict"するとthisはundefinedになるが、ここでは"use strict"していないものとして進める)

オブジェクトメソッド内で this と書いた場合は、そのメソッドを呼び出した時に、メソッド名の左に書かれたオブジェクトが this の指す先となる。

上のコードでobj.method()をそのまま呼んだときは「my object」が表示される。
このとき、thisはobjオブジェクトを指していることになる。
しかし、obj.methodを変数fnに格納し、それを呼ぶと undefined が表示される。
中身がまったく同じ関数(メソッド)であっても、それが「どのように呼ばれたか」で、その関数に書かれた this の指し示す先が分かれるのだ。

JavaScriptの this は、その関数が呼ばれた時に、関数名の左側に書かれたオブジェクトを指す。
つまりobj.method()として呼び出した時は、this は obj を指す。
しかし、obj.methodを一度 fn という変数に格納し、fn()として呼び出すと、this は window を指す。
なぜならfn()として呼んだときに、関数名 fn の左には何も無いからだ。
何も無いとき、JavaScriptはグローバルオブジェクト(window)が省略されているという解釈をするので、この場合はwindow.fn()と解釈される。
つまり this は window を指すことになり、 window.a を表示しようとするが、window に a というプロパティは存在しないので undefined という結果になる。
これが呼ばれたその時に決まるわけだ。

なので、オブジェクトの中に書いたメソッド内でコールバック関数を書き、その中でthisを書いていた場合、そのコールバック関数を他の関数に渡すと this が window を指してしまうことになる。

JavaScriptの関数はすべてクロージャになっているので、自身の外側スコープにある変数や関数をほぼ全部取り込むのだが、this だけは例外で取り込まず、関数が呼ばれた時にthisがどこを指すかが決定される。
この仕様があるため、コールバック関数が思ったように動いてくれない、ということが起こる。

ではどうすれば良いかというと、ES6から使えるようになったアロー関数式でコールバック関数を書けば良い。
アロー関数式は this を含むすべてをクロージャに取り込むので、コールバック関数内でも期待した通りの this として振る舞ってくれる。

ES6以前の昔は、 this を一旦普通の変数(that や self という変数名が分かりやすいが、なんでも良い)に格納しておき、その変数経由でクロージャに取り込んで使っていた。


[ 戻る ]
saluteweb