LoginSignup
8
2

More than 3 years have passed since last update.

JavaScriptのthisとは?

Last updated at Posted at 2019-12-11

こちらは、 CAMエンジニア Advent Calendar 2019 12日目の記事です。
昨日は maruken24 さんの レンダリングパターンをいらすとやで解説してみた でした。

こんにちわ!KeitaroArataです。
Qiita初投稿させていただきます。

今年の春に新卒フロントエンジニアとして入社させていただき、
Web未経験ですがサービスに携わらせて頂いております。
駄文ですが、最後まで読んでいただき、ご指摘などいただけると嬉しいです。

概要

半年ほど前にthisが変わる問題みたいなことを先輩が言っていたことを思い出しました。
どうやら、メソッドの中でthisを使うと、想定とは違った挙動をすることがあるようです。
今回はその問題に対して目を向けていこうと思います。

thisで呼んでいるのに…

this.__nickname = "kebosu"
const object = {
  console: function() {
    console.log(this.__nickname);//undefined
  }
}
object.console();

上記のようなコードなんですが、みなさんこちらって何が帰ってくると思いますか…?
僕はなんとなくで書いたんですけど、これってundefinedが返ってくるんですよ。不思議ですね。
直感的にはkebosuが返ってくる事が期待されるこのコード。
さて、この問題を解決し、kebosuを出力させるには一体どうしたら良いのでしょうか。
まずは、この出力しているthisは一体何者なのかを調べる必要がありそうです。

console.log(this);をsetTimeout内のfunctionに入れてみると…
Object {console: function(...)}と、objectの中身が出力されました。
おや?ということは
理想:グローバルオブジェクトを参照
現実:オブジェクトの中身を参照
となっているというわけですね。
さて、ここでthisという曖昧なものをはっきりとさせましょう

thisとは

thisとは呼び出される場所によって姿を変える独特な変数のことです。
1. 関数内のthisは,グローバルオブジェクトのままである
2. メソッド内のthisは,そのメソッドが属するオブジェクトを指す
3. コンストラクタから呼び出したthisは,そのコンストラクタが生成したオブジェクトを指す

大体のthisが上記の三パターンに分類できるそうです。
(ちなみに僕ここで初めて知ったんですが、関数とメソッドって別物なんですね…)
12/12追記:別物というか、関数の一部をメソッドと呼ぶ感じですね。

さらにthisを操作する方法として、bindというものがあります。
bindとは、thisの参照先を変更する事ができる関数です。


this.__nickname = "kebosu"
const object = {
  console: function() {
    console.log(this.__nickname);//kebosu
  }.bind(this)
}
object.console();

と、bindしてあげることで、無事kebosuが出力されましたね。
thisの参照先を指定してあげることで問題は解決しました。
万事解決と言いたいところですが、上記のコードだった場合は、わざわざbindしてあげる必要もないのです。


this.__nickname = "kebosu"
const object = {
  console: () => {//arrow function
    console.log(this.__nickname);//kebosu
  }
}
object.console();

このように、アロー関数を使ってあげれば良いのです。

アロー関数

ざっくりいうと関数式(関数リテラル)をより簡潔にかける記法のことです。
アロー関数というのは具体的には下記のようなものです。


//es6
const hoge = () => {
  console.log("hoge")
}

hoge();//"hoge"が出力;

これはes6から使えるようになった記法です。
ちなみにコレをes5に変換すると↓


//es5
var hoge = function hoge() {
  console.log("hoge");
};

hoge();//"hoge"が出力;

見比べてみると、アロー関数の方が簡潔に記述されている印象があり、スッキリした書き方ですね。
引数にfunctionが入るもの(mapforeach等)にもこの記法は有効です。
という、上記程度の知識でしかアロー関数式というものを認識していませんでした。
この機会にMDNを見てみると

アロー関数式は、より短く記述できる、通常の function 式の代替構文

最初に言っていた、簡潔な書き方というのは正しいようですね。安心しました。
しかし、さらに読み進めて行くと…

アロー関数自身は this を持ちません。レキシカルスコープの this 値を使います。

レキシカルスコープというワードが出てきましたね。
次にレキシカルスコープの話をしましょう。

レキシカルスコープ

まず、ローカル変数の有効範囲を決める概念としてレキシカルスコープとダイナミックスコープというものがあります。
・レキシカルスコープ...関数を定義した時点でスコープが決まる
・ダイナミックスコープ...関数を呼び出した時点でスコープが決まる

言語ごとにスコープの仕様は決まっているようで、JavaScriptはレキシカルスコープです。
前章の話と繋げると、アロー関数式は関数を定義した時点でのthisを用いるようです。
つまりはアロー関数式を定義した際のスコープでthisが決まるということですね。


const object = {
  num: 1,
  fnA: function fnA(){console.log(this.num)},
  fnB: () => {console.log(this.num)}
}

object.fnA();//1
object.fnB();//undefined

こちらを見ていただけるとわかりやすいですが、従来のfunctionとアロー関数で挙動が違うことがわかると思います。
・function: 内部のthisは実行時のレシーバであるオブジェクトになる
・アロー関数: 内部のthisは宣言時のスコープを持つオブジェクトになる

今回の例だと、objectがレシーバオブジェクトであり、functionではobject内のnumがthisの参照先になっているということですね。
一方、アロー関数は参照がグローバル(window)オブジェクトになっているため、windowオブジェクトのnumを参照しようとしていますが、定義されていないためundefinedが返ってきています。
そのため、varで変数を用意してあげると↓


const object = {
  num: 1,
  fnA: function fnA(){console.log(this.num)},
  fnB: () => {console.log(this.num)}
}
var num = 2;
object.fnA();//1
object.fnB();//2

のような結果になります。
this、奥がふかいですね。

まとめ

・thisとは呼び出した場所によって中身が変わる変数
・アロー関数とはes6から生まれた、関数宣言を簡潔にかける方法である。
・jsはレキシカルスコープである。
・アロー関数はthisの参照元が宣言時にbindされる

最後に

今回、thisについてお話ししましたが、setTimeoutと絡めてあげたらどんな挙動をするのかだったり、クロージャの話など、展開できる話題は多くあります。
今後も機会があれば記事にまとめて、あとで見返せるような形で知識を広げていきたいです。
今回は、初の記事ということで、稚拙な文章を見せてしまったかもしれないですが、ここまで読んでいただき誠にありがとうございました。

明日は @kita21 の記事になります
お楽しみに!

参考

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Functions/Arrow_functions
https://developer.mozilla.org/ja/docs/Web/JavaScript/Closures
https://qiita.com/shoichiimamura/items/aec874fc5cf55cde7fa1
https://qiita.com/mejileben/items/69e5facdb60781927929
https://qiita.com/t-tonchim/items/07d763325c7cb659efb7
https://wemo.tech/904
http://enlosph.hatenablog.com/entry/2017/01/20/222605

8
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
2