JavaScriptの「this」やその他気になっていたこと

JavaScriptの他の言語にはあまり見ない特徴として「this」があげられます。今回は「this」とその周辺のアロー関数の話やbindについてまとめてみたいと思います。

thisとは何なのか

javascriptのコードを見るとよくこんな感じの書き方をしているのを見ると思います。

const test = {
  prop: 42,
  func: function() {
    return this.prop;
  },
};

console.log(test.func());

実行するとconsole.log()で42が出力されます。つまりこの時のthisはtestオブジェクトを指していることがわかります。これからもわかるようにthisは関数呼び出し元のオブジェクトを指しているのです。リファレンスには次のように書かれています。「ほとんどの場合、this の値はどのように関数が呼ばれたかによって決定されます (実行時結合)。これは実行時に代入によって設定することはできず、関数が呼び出されるたびに異なる可能性があります。」(this - JavaScript | MDNから引用)
例えば、以下のようなコードはどうでしょうか。

element.addEventListener("change", {
      console.log(this)
});

この時のthisも先ほどの考えと同じようにaddEventListener関数の呼び出し元のオブジェクトであるelementを指します。(addEventListenerについては次の記事を参考にしてください。JavaScript | addEventListenerメソッドを使ってイベントリスナーを登録する

thisの注意点

もし関数の呼び出し元がない場合(つまりobj.function()におけるobj.が省略された場合)はthisにはグローバルオブジェクトが入ります。strict モードでは、undefinedです。「呼び出し時に this の値が設定されないため、this は既定でグローバルオブジェクトとなり、これはブラウザーでは window です。~(中略)~ ただし strict モードでは、実行コンテキストに入るときに this 値が設定されていないと、以下の例のように undefined のままになります。」(this - JavaScript | MDNから引用)

function test() {
    console.log(this);
}

test();
アロー関数について

アロー関数とは新しくJavaScriptに導入されたの関数の書き方です。アロー関数の書き方については知っていたのですが、なぜこの書き方を使うのかいまいちわかってませんでした。アロー関数のメリットとして上で述べたthisが大きく関わっていました。
参考になった記事です。アロー関数が便利な理由と使いどころ - Qiita
簡単にまとめるとアロー関数を使う利点として①簡潔に関数を記述できる」②「thisを束縛しない」ということが言えるようです。①はわかります。②のthisを束縛しないとは何なのでしょうか?以下の文言がしっくりきました。
「アロー関数を使えば、thisの値は関数定義時に決める事ができる」「普通の関数(アロー関数でない関数)の場合、thisの定義は定義した時点でなく、実行時に決まります。(←ここ重要)」(アロー関数が便利な理由と使いどころ - Qiitaから引用)
上で述べたように通常の関数の場合はthisは関数の呼び出される時に決まるということです。そして呼び出し元がない場合はthisはグローバルオブジェクトになります。つまりオブジェクトのメソッド内で関数呼び出しをしている時などはthisはそのオブジェクトではなくグローバルオブジェクトになるということです。以下のconsole.log()の中身の所です。

const test = {
  prop: 42,
  func: function() {
    const test_func  = function(){
         // window or global
         console.log(this)
    }
  },
};

これをアロー関数で書き換えるとどうなるかと言うとthisはその関数(test_func )が囲まれているメソッドfuncと同じthisになります。

const test = {
  prop: 42,
  func: function() {
    const test_func  = () => {
         // test オブジェクトになる!
         console.log(this)
    }
  },
};

これらのことからアロー関数と通常の関数の違いが理解できると思います。

thisを束縛しないとは

上で具体的に述べたアロー関数の特徴「thisを束縛しない」ですが結局のところ、この言葉の意味はどういうことなのか、ということがしっくりきていませんでした。次の記事を参考にすると

このようにthisは呼び出し元によって値が変動します。
関数オブジェクトの呼び出し方によって定められた値が、強制的にthisに代入されるためです。
アロー関数はこの強制的な代入を行わないようにします。
(中略)
「thisを束縛する」  === 「thisに強制的に値を代入する」
「thisを束縛しない」 === 「thisに強制的に値を代入をしない」

アロー関数のthisを束縛しないの意味とは - Qiitaから引用)ということらしいです。

bind()について

先ほどアロー関数を使うとthisを束縛せずに関数を実行できると述べましたが、bind()メソッドを使ってもthisが何を表すのかを関数に指し示す(教えてあげる)ことができます。次のコードを見てください。

const test = {
  prop: 42,
  func: function() {
    console.log(this)
 console.log(this.prop)
  },
};

const test_obj_func = test.func;
test_obj_func(); 

この場合のthisはグローバルオブジェクトです。それではbind()を使ってtest_obj_func()にthisの値を教えてあげます。以下のようになります。

const test = {
  prop: 42,
  func: function() {
    console.log(this)
 console.log(this.prop)
  },
};

const test_obj_func = test.func.bind(test);
test_obj_func(); 

参考にした記事です。上で述べたこと以外にも様々な例やcall()関数についても分かりやすく書いてあります。
【初心者向け】JavaScriptのbindって何??を理解する(call, appyも一緒) | アールエフェクト