【JavaScript】thisについて理解しよう(関数呼び出し編)

JavaScript

thisとは

thisとは呼び出し元のオブジェクトのことである。

JavaScriptのthisの挙動はややこしく、thisって一体何??と
なっている人も沢山いると思います。
thisとは一言でいうと呼び出し元のオブジェクトになります。

thisにもいろいろ種類があるのですが、今回はthisの中で一番意味を理解しやすい関数で呼び出された際のthisの説明をします。

まず.の前にあるものは全てthisになると考えてください。
person.introduce()←personがthisになる。
teacher.teach() ← teacherがthisになる。

挙動がややこしいものに関しては実際に動画で挙動をみたり自分で打ってみて確かめるのが一番理解できます!
今PCでこの記事を見ている人は下記のサイトからJavaScriptの挙動を確認できるので、thisの動作を自分でも打って確認しましょう。

練習しよう

練習画面

こちらの動画でいうと、person.tall_show()としたときに、.の手前なので、personがthisになり、this.tallとしているので、これはthis.tall == person.tallになることが分かると思います。

javascript
1
2
3
4
5
6
7
person = {
  tall: 175,
  tall_show: function() {
    console.log(this.tall);
    console.log(person.tall == this.tall)
  }
}

これが一番わかり易いthisの例です。.の手前になるものがthisということですね。
それでは、.の前に何もない場合はどうなるでしょう。

this

javascript
1
2
3
4
5
window.width = 1280;
function window_width_show() {
  console.log(this.width)
}
window_width_show()

なぜ1280と表示されるかというと、thisは.の前に何もない場合は、windowオブジェクトを参照するようになっております。
なので、window.width = 1280としておくと、windowオブジェクトのプロパティにwidthというものが追加されます。

window_width_show()メソッドは.というものがないので、何も参照してませんね。
この場合にはthisはwindowオブジェクトを参照することになっているので、window.widthに設定した値がthis.widthに出力されるというわけです。

ではでは、関数の中からまた別の関数を呼び出された場合のthisはどうなるのでしょう。

別の関数から呼び出されたthis

javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function test1() {
  this.value = 'test1';
  console.log(this.value);
}

function test2() {
  this.value = 'test2';
  console.log(this.value);
}

person = {
  test_show: function() {
    test2();
  }
}

person.test_show();

動画にある通り、test_show()メソッドがtest1()メソッドを呼び出している時はtest1()メソッドで定義されたthis.valueが呼び出されています。
test2()メソッドが呼び出されている時は、test2()メソッドで定義されたthis.valueが呼び出されています。

ただ勘違いして欲しくないのは、test1()メソッドにも.というものがないので、test1()メソッドの中でのthisはtest1()のメソッドのvalueではなくて、windowオブジェクトになります。

test2()メソッドもしかりです。
つまり動画でいうと、test2()メソッドはtest1()メソッドで定義したwindowオブジェクトのvalueプロパティを上書きしているような形になるわけです。

例え関数の中で関数を呼び出すような入れ子になっていても.とオブジェクトのプロパティとして定義していない限り、thisを使えばwindowオブジェクトが参照されるわけです。

this

ではでは、関数が入れ子の状態で呼び出される時、.が付いていなければ、入れ子でもthisはwindowオブジェクトになっていましたが、.が付いていれば.の前に付いているオブジェクトがthisとして参照されるのでしょうか?
確かめてみましょう。下記の動画を見てください。

this

動画を見てみると分かると思うのですが、studentという新たなオブジェクトを追加します。
studentを関数で呼び出した場合は、this.valueの値はwindowオブジェクトのvalueというプロパティではなくてstudentオブジェクトのvalueというプロパティに1000と追加されたのが分かると思います。

つまりコンソールに出ているtest2は、windowオブジェクトのvalueプロパティ
コンソールに出ている1000は、studentオブジェクトのvalueプロパティ
.の前に付いている場合は.の前に付いているオブジェクトがthisになる。
.がない場合はwindowオブジェクトがthisになる。
というのが分かって頂けたと思います。

javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var student = {
  value: 1000,
  score_show: function () {
    console.log(this.value);
  }
}

function test1() {
  this.value = 'test1';
  console.log(this.value);
}

function test2() {
  this.value = 'test2';
  console.log(this.value);
}

person = {
  test_show: function() {
    test2();
    student.score_show();
  }
}

person.test_show();

ただ、JavaScriptのthisって関数の中で何も指定してなかったら、thisがwindowオブジェクトになるって面倒臭くないですか?
例えばこんな感じです。
これだと、コンソールに
「windowに関して教えます。」と表示されます。

javascript
1
2
3
4
5
6
7
8
9
10
11
12
var content = 'windowに関して教えます。'
var teacher = {
  content: '数学に関して教えます。',
  show_teach_content: function () {
    function content () {
      console.log(this.content)
    }
  content();
  }
}

teacher.show_teach_content();

teacherオブジェクトの中で呼び出されるthisは関数を直接呼び出してもwindowではなくて、thisをteacherオブジェクトで扱いたいと思いませんか?

つまり上記の例だとteacher.show_teach_content();と呼び出しておりteacherオブジェクトの中でshow_teach_content()が実行されます。

その中で直接contentというメソッドを呼び出されるので、このcontentメソッドはwindowオブジェクトに紐づいています。
「windowオブジェクトのcontent→windowに関して教えます。」と表示します。

ただ上記ではなく、「teacherオブジェクトの中で呼び出されているので数学に関して教えます。」
と表示されてほしいと思います。
ただcontentメソッドの.の前に何もないのでwindowオブジェクトが参照されてwindowオブジェクトのcontentとして表示されています。

そこで関数から呼び出された時にthisの値をwindowオブジェクトではなく、呼び出されたオブジェクトのthisにする方法が二つあります。
一つ目は変数に入れる方法です。

下記の様にshow_teach_content();の中でthisを何かの変数に入れれば、その変数はどこにも紐づいていない関数でもteacherオブジェクトのthisとして扱うことができます。

今回はselfという変数に入れましたが、分かり易ければ何でも良いです。
ただ僕が働いている会社ではselfを使っていて、他のところでもselfという記述はよく見かけるので、特にこだわりがなければthisを入れる変数はselfで良いかと思います。

javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
var content = 'windowに関して教えます。'
var teacher = {
  content: '数学に関して教えます。',
  show_teach_content: function () {
    var self = this;
    function content () {
      console.log(self.content)
    }
  content();
  }
}

teacher.show_teach_content();

this

二つ目の方法はbindメソッドを使います。
この方法もよく見かけます。

bindメソッドとはbindメソッドの前にあるオブジェクトのthisを捕縛することができます。
ん??と言っても良くわからないですよね。。

例えば下記のコードがあるとして

javascript
1
2
3
4
function bind_test () {
  console.log("bindする値は" + this + "です。")
}
bind_test.bind('programan')()

ここで表示されるのは「bindする値はprogramanです。」
と表示されます。

本来であれば.の前に何もなければthisオブジェクトはWindowオブジェクトを参照することになっているので、「"bindする値は[object Window]です。"」
と表示されるはずですが、
なぜ「bindする値はprogramanです。」と表示されたのか?

その理由はbind_test.bind('programan')とここの部分で、bind_testの中で参照されるthisは'programan'に変更しますよ!
という意味になるからです。
なので、本来はthisオブジェクトが参照するのはWindowオブジェクトのはずですが、bind_test.bind('programan')で

javascript
1
2
3
4
function bind_test () {
  console.log("bindする値は" + this(bindメソッドによりプログラマンに変更) + "です。")
}
bind_test.bind('programan')()

となるわけです。
bind_test.bind('programan')()
↑こちらのコードの意味を解説しておくと、bind_test.bind('programan')で、bind_testの中のthisがprogramanに置き換わった、
bind_test.bind('programan')()←ここの()の部分は、thisが'programan'に置き換わったbind_testメソッドを実行しているということになります。

直接呼び出す事もできますが←bind_test.bind('programan')()
実際には変数に入れる事もよくあります。

javascript
1
2
3
4
5
6
function bind_test() {
  console.log("bindする値は"+ this + "です。")
}

programan_introduce = bind_test.bind('programan')
programan_introduce();

こちらもbind_test.bind('programan')()と同じ表示結果になります。
programan_introduce = bind_test.bind('programan')
↑この部分で、programan_introduceという変数には、bind_testの中のthisオブジェクトがprogramanに変わった状態の関数が入っていることになります。

その関数をprograman_introduce()という形で呼び出すことにより表示ができるということですね。

では、ここでbindの使い方が分かったところで、関数を入れ子にした少しややこしい状態でbindメソッドを実行する動画を下に載せたので実際に自分でも打って動作を確かめましょう。

bindを変数に入れずに直接呼び出す形
this
bindを変数に入れて呼び出す形
this

まとめ

.の前に何もなければthisはwindowオブジェクトが参照されます。
.がある場合は.の前に付いているものがthisとして参照されます。
thisをwindowオブジェクトではなく、呼び出し元のthisに捕縛するには selfという変数に入れるのが良いです。
thisをwindowオブジェクトではなく、呼び出し元のthisに捕縛するには bindメソッドを使うと良いです。