JavaScriptのthisは、他の変数とは異なり少し特殊な動作をします。

そのため、JavaScriptを学習中の人にとってはthisの概念を理解するのは容易ではなく、なんとなくthisを使っていたり、何のためにthisが使われているのか理解するのに時間がかかる場合があります。

そこで今回は、thisの参照先やどのような用途で使われるのか具体的に解説していきます。

thisとは

「this」とは、関数の中で使われる読み取り専用のキーワードです。thisの値は、基本的に関数が実行される際に確定します。そのため、関数がどのように呼び出されたかによってthisの値が決まり、関数が呼ばれる度に値が異なることがあります。

このように特殊な性質を持つことから、JavaScriptでは便利な変数として利用されています。一方、言葉だけでは理解しにくいため、thisがどのように動作をするのか用途ごとに見ていくと分かりやすいです。

thisの振る舞い

ここからは、thisの参照先がどのように決まるのか、解説していきます。

グローバルコンテキスト

「グローバルコンテキスト」とは、ファイルのトップレベル(もっとも外側のスコープ)に書かれたコードのことで、トップレベルにあるthisは、グローバルオブジェクトを参照します。

グローバルオブジェクトは環境ごとに異なりますが、ブラウザで実行する場合は「windowオブジェクト」となります。

console.log(this); //Window
console.log(this === window); //true

this.hello = 'Hello';
console.log(window.hello); //Hello

関数コンテキスト

「関数コンテキスト」とは、関数の内側に書かれたコードのことを指します。
関数の内側にあるthisは、通常、関数の呼び出し時に値が決まるという特徴があります。

非strictモードとstrictモード

strictモードが設定されていない場合、関数内のthisはグローバルオブジェクトを参照します。
つまり、トップレベルにあるthisと同じ結果となります。

function test() {
  console.log(this); //Window
  console.log(this === window); //true
};
test();

一方、strictモードが設定されている場合、実行コンテキスト内でthisの値の指定がなければ、以下のようにundefinedとなります。

function test() {
  'use strict';
  console.log(this); //undefined
  console.log(this === window); //false
};
test();

関数宣言と関数式

上記では、strictモードの有無によってthisの値が異なることが分かりました。
では、関数の種類によってthisの参照先に違いが出るのでしょうか。
関数宣言と関数式によるthisの動きを見てみましょう。

以下では、関数宣言の「test1()」と関数式の「test2()」が定義されています。

//非strictモード
function test1() {
  console.log(this);
};
test1();
//Window

const test2 = function() {
  console.log(this);
};
test2();
//Window

//strictモード
'use strict';

function test1() {
  console.log(this);
};
test1();
//undefined

const test2 = function() {
  console.log(this);
};
test2();
//undefined

どちらも同じ結果となります。

これらは、実行コンテキスト内で値の指定がない(ベースオブジェクトがない)ため、thisの値は、undefinedまたはwindowオブジェクトという結果になっています。
そのため、メソッドを使わない単純な関数内でthisを使うメリットはあまりありません。

つぎのメソッドの項目を見てみるとより違いが分かります。

メソッド

JavaScriptでは、オブジェクトのプロパティとして定義されている関数のことをメソッドと呼びます。
そのため、メソッド内のthisは、自身のオブジェクトを参照することになります。
言葉だけだと理解しにくいかもしれませんが、以下の実行結果を見るとどうでしょうか。

const test = {
  id: 1,
  name: '田中太郎',
  user: function() {
    console.log(this);
  }
};
test.user();
//{id: 1, name: '田中太郎', user: ƒ}

testという名前のオブジェクト内に定義した関数user(これをメソッドと呼ぶ)では、その中にあるthisをコンソールで出力しています。
そして、メソッド内にあるthisがtestを参照しているという結果となっています。

コンストラクタ

コンストラクタは、thisが使われる代表的なケースです。
コンストラクタ内のthisは、new演算子を使って作成されたインスタンス(新しいオブジェクト)と関連付けられます。
この時、「this.○○」と記述することで、インスタンスのプロパティを定義することができます。

function Test(id, name) {
  this.id = id;
  this.name = name;
};
const user = new Test(1, '田中太郎');
console.log(user);
//Test{id: 1, name: '田中太郎'}

結果を見ると、コンストラクタ内のthisがインスタンスのプロパティを参照していることが分かります。

アロー関数

アロー関数におけるthisの振る舞いは特殊なため、通常の関数との違いにも少し触れておきます。

通常の関数の場合、関数の呼び出し時によってthisの値が決まり、どのように呼び出されたかによってthisの値が異なると説明しました。
一方で、アロー関数内のthisの場合は、関数が定義された時点で値が決まります。

以下の例を見比べてください。

通常の関数:

this.val = 'global';

function test1() {
  console.log(this.val);
};

const obj = {
  val: 'object',
  func: test1
};
obj.func();
//object

アロー関数:

this.val = 'global';

const test2 = () => {
  console.log(this.val);
};

const obj = {
  val: 'object',
  func: test2
};
obj.func();
//global

いかがでしょうか。

アロー関数内のthisは、呼び出し元に関わらず、アロー関数の外側で定義されたthisの値が参照されています。
このような振る舞いをすることから、thisの値を束縛するという表現がされることがあります。
thisの値が固定化されているため、より直感的です。

まとめ

今回は、JavaScriptにおけるthisの振る舞いを見ていきました。thisは、関数の種類や用途によって値が変わる特殊なキーワードです。

意図しない挙動を発生させないためにも、thisの振る舞いや参照先を理解しておくことは大切です。thisの挙動を一度で理解するのは難しいため、使いながら少しずつ覚えていきましょう。

JavaScript関連記事