JavaScriptのthisの性質を知ろう! ~ 脱初心者をしたい方向け ~

こんにちは。TECH DRIVEの小笠原です。 JavaScriptを書いていると度々目にする「this」ですが、その性質に癖があるため、JS初学者の方にとってハードルになりがちです。
本記事では、JS中級者へのステップアップを目指される方に向けて、thisの性質を知っていただくための記事を書かせていただきました。

thisの性質を知ろう

誤解を恐れず言うなら、thisとは「JavaScriptが予め用意している変数である」と言えます。
基本的にthisは、グローバルオブジェクトを指します。
例えば、以下のようにグローバルスコープでthisを参照するとWindowオブジェクトを返します。(※ クライアントサイドで動作するJavaScriptの場合)

console.log(this) // Window {省略}

また、以下のようにグローバルスコープ直下に定義した関数内でthisを参照した場合も、同じくWindowオブジェクトがthisの値となります。

function hoge () {
  console.log(this)
}
hoge() // Window {省略}

このようにthisは、基本的にグローバルオブジェクトを指します。
※ ただし、「strictモード」が有効となっている場合、関数内のthisは基本的にundefinedとなりますので、注意が必要です。
ここで「基本的に」とつけたのは、thisの値が上記の限りではないためです。
本章の冒頭で述べたようにthisは変数であるため、その値が一定のものを指すとは限らないのです。

thisが変化する4種類のパターンを知ろう

関数内のthisは、関数の呼び出し方によって値が変化することがあります。
抑えておくべきは、以下4パターンで関数が実行された場合、いずれもthisの値が異なるという点です。

  • 1.関数として呼ばれた場合
  • 2.メソッドとして呼ばれた場合
  • 3.コンストラクタ関数として呼ばれた場合
  • 4.call, applyから呼ばれた場合

それでは順番に見ていきましょう。

1. 関数として呼ばれた場合

これは、冒頭で例としてあげたグローバルスコープに定義された関数内での話になります。
先ほどお伝えした通り、この場合thisはグローバルオブジェクトを返します。 ※ 繰り返しとなりますが、「strictモード」が有効となっている場合、thisはundefinedとなります。

2. メソッドから呼ばれた場合

次は、あるオブジェクトに属するメソッド内で参照されるthisについてです。
例えば以下のようなオブジェクトがあるとします。

var person = {
  name: 'Ken',
  greet: function() {
    console.log('My name is ' + this.name)
  }
}

さて、上記のコードの次に person.greet() を実行した場合、結果はどうなるでしょうか?
「1. 関数の中で呼ばれた場合」同様に、thisがグローバルオブジェクトを指すのであれば、Windowオブジェクトのnameプロパティが参照され「My name is Window」となってしまいそうですね。

しかし、greetメソッドの実行結果は「My name is Ken」となります。
これは、あるオブジェクトのメソッド内で呼ばれたthisは、メソッドが所属するオブジェクトを指すようになるためです。
そのためgreet内でのthisは、person(厳密には変数person代入したオブジェクト)となるため、上記のような結果となります。

3. コンストラクタ関数から呼ばれた場合

次にコンストラクタ関数内でのthisについてです。
本章では、thisと合わせコンストラクタ関数に関する最低限の情報も合わせてお伝えしたいと思います。
コンストラクタ関数とは、乱暴な言い方をしてしまうとnewというワードを使用し実行する関数になります。
まずは、以下のコードを見てください。

function Person (gender) {
  this.gender = gender
  this.isMale = function() {
    return this.gender === 'male'
  }
}

// 通常の関数として実行
Person('male') // 結果: undefined

// コンストラクタ関数として実行
var person = new Person('male') // 結果: Person {gender: "male", isMale: ƒ}
// コンストラクタ関数の返り値(インスタンス)に含まれるメソッドを実行
person.isMale() // 結果: true

上のコードでは、Personという関数を定義し、まずは通常の関数として実行しています。
この結果は「undefined」※ となります 。
※ JavaScriptでは、関数内で明示的に返り値を指定(return)していない場合、関数はundefined(未定義値)を返す仕様になっているのです。

しかし、コンストラクタ関数として実行した場合、関数の返り値がオブジェクトになっていることが確認できます。
これは、コンストラクタとして実行した関数は、内部で動的に生成したオブジェクト(これをインスタンスと言います)を返すようになるためです。
そしてこのインスタンスの中には、関数内で「this.プロパティ名=値」とした結果が、プロパティ/メソッドとして入っていることが確認できます。
上のコードではnew Person('male’)の結果(インスタンス)を変数personに代入していることになります。
故に最終行では person.isMale() が正常に動作していることが確認できます。
ここでは、コンストラクタ関数内でのthisは、自身が返すインスタンスを指すことを抑えておきましょう。

4. call, applyから呼ばれた場合

さて、ここまでthisの性質と合わせ様々な関数の呼び出し方をみてきました。
しかし、関数の呼び出し方は、これだけではありません。
本記事において詳細は割愛しますが、JavaScriptでは、callやapplyといった「関数を実行するための関数」が存在します。
callやapplyは以下のように使用され、関数を実行する際にその関数内におけるthisを第一引数で渡した任意の値にすることができます。

関数.call(thisに指定する値, 呼び出した関数に渡す引数情報)

call/applyの理解については、本記事の内容はもちろん、オブジェクトに関するより深い理解も求められます。
そのため本記事で詳細に触れることはしませんが、call/applyが気になるという方は、以下の記事をご一読ください。

dev.techdrive.top

ここでは、call/applyを使用し関数を実行した場合、関数内でのthisの値は、プログラマが指定した任意の値になるということを覚えておきましょう。

おまけ Class内でのthisについて

JavaScriptのClassを使用したコードを見たことがある方は、その内部でもthisが使われているのを目にしたことがあるのではないでしょうか?

Class内でのthisは、先程ご紹介した「コンスラクタ関数内でのthis」とほぼ同じと言えます。
Class内のメソッドから参照するthisは 「new Class名」 実行時に生成されるインスタンスを指すことを覚えておきましょう。

まとめ

いかがでしょうか? JavaScriptの学習を進めていると必ず出てくるthisですが、本記事でもご紹介した通り、関数内のthisは「関数の呼出し方」によって値が変わります。
このthisの柔軟性が学習時のハードルになりがちです。
本記事を通して、少しでもthisへの理解を深めていただけたのなら幸いです。

JavaScript Climbing

JavaScriptに特化したトレーニング「JavaScript Climbing」を行なっています。
this、クロージャ、Class等、中〜大規模のフロントエンドの開発にのぞむにあたり、必須となるスキル習得を現役のWEBエンジニアが徹底してサポートします。
2週間の無料体験期間がございますので、ご興味がある方は是非以下のリンクより詳細をご覧ください。

circlearound.co.jp

個別トレーニング

短期間でぐっと成長したい方は弊社主催の個別トレーニングがおすすめです。 トレーニング内容は、受講者の方の課題/要望をお伺いした上で、フルオーダメイドで作成させていただきます。 詳細は以下のリンクよりご確認ください。(応募者多数の場合には時間を別途ご用意する予定です)。

WEBプログラミング個別トレーニング

TECH DRIVEについて

TECH DRIVEは「技術者の成長を加速させる」をキーワードに都内で活動をしているコミュニティです。
TwitterやFacebookにて技術ネタやイベント情報の発信を行っていますので、ご興味があれば、いいねやフォローをお願いいたします。