アロー関数

アロー関数について勉強します。

基本の書き方

まずはおなじみの、公式サイトからの引用です。

アロー関数式は、より短く記述できる、通常の function 式の代替構文です。また、this, arguments, super, new.target を束縛しません。アロー関数式は、メソッドでない関数に最適で、コンストラクタとして使うことはできません。

なるほど。後半部分(また、this,~ )についてはいったん置いておくとして、前半部分について考えてみます。

通常の function 式の代替構文です。

通常のfunction式の代替構文とのことなので、まずはアロー関数とfunction式を比較してみましょう。
以下で両者の書き方をそれぞれ試していますが、挙動は同じものになります。

//アロー関数
let double = (n) => n * 2;

//function式
function double(n){
  return n * 2;
}

//どちらもdouble(n)で呼び出せる
console.log(double(3));

また、上記のアロー関数はこの書き方以外にも、引数の個数などで、記述の方法が異なります。 以下、例を示します。

// returnを書かない場合はブロックの記述は不要
(a, b) => a + b;
//returnを書く場合はブロックで囲む
(a, b) => {return a + b;}
//引数が1つの場合は()が不要
n => n * 2;
//引数がない場合は()が必須
() => console.log('hello!');

ここまでが基本的な書き方ですね。
さて、公式ドキュメントでは以下のようにも述べられています。

2 つの理由から、アロー関数が導入されました。1 つ目の理由は関数を短く書きたいということで、2 つ目の理由は this を束縛したくない、ということです。

1つ目の理由については視覚的にも明らかですね。それでは2つ目の「thisを束縛したくない」とはどういったことでしょうか。

thisを束縛しない

そもそも、thisを束縛するってのがわからないんですよね・・・。
以下、公式ドキュメントより。

アロー関数以前は、関数の呼び出し方法に応じて自身の this 値を定義していました

つまり、thisをどのオブジェクトに紐付けるか・・例えばグローバルなのか、自身のインスタンスなのか。そう言ったことをthisの束縛というふうに理解できます。*1

それではまずは、「thisが束縛される」コードを実際に見てみましょう。(アロー関数より前の関数定義方法)

//コンストラクタの場合
function person(name, age) {
  //このthisは自身のインスタンスを指す
  this.name = name;
  this.age = age;
  
  setInterval(function growUp() {
    //このthisはpersonインスタンスを指さずに、
    //グローバルオブジェクトを指す。
    this.age++;
    //グローバルオブジェクトにageはないので、NaNとなってしまう
    console.log(this.age); 
  }, 1000);
}

taro = new person('taro', 20);
console.log(taro)

//関数の場合
function hoge() {
  //'use strict'を用いて厳密モードにすると、
  'use strict';
  //このthisはundefinedとなる
  console.log(this);
}

function huga() {
  //'use strict'を用いない場合、thisはグローバルオブジェクト(この場合はWindowオブジェクト)を指す
  console.log(this);
}

上記の通り、関数の呼び出し方法によって、thisの指すオブジェクトが変化していきます。これが、「thisを束縛する」という意味なんですね。

それでは次に、「thisを束縛しない」、アロー関数についてもコードで試してみます。 公式サイトでは、以下のように記述があります。

アロー関数内の this 値は通常の変数検索ルールに従います。このためスコープに this 値がない場合、その一つ外側のスコープで this 値を探します。

比較しやすくするため、前述したPersonのコンストラクタ関数で実施していたgrowUp()関数をアロー関数で書き換えてみましょう。

function person(name, age) {
  this.name = name;
  this.age = age;
  //アロー関数に変更
  setInterval(() => {
    //前述したスコープの探索ルールに基づき、personオブジェクトを指す
    this.age++;
    console.log(this.age);
  }, 1000);
}

元々、コンストラクタ関数内に定義していたgrowUp()をアロー関数に直したことで、this.age++がpersonインスタンスのもつageプロパティを加算することができるようになりました。 このように、アロー関数を使用すると、thisが束縛されなくなるため、開発者の直感にある程度従った挙動にできるメリットもあります。

終わりに

駆け足でしたが、アロー関数について学びました。
JavaScriptも変化が早くて、ついていくのが大変ですね。

*1:これを読むと良いかもしれません