JavaScriptアロー関数の使い方とthisの挙動について解説

JavaScriptのアロー関数について説明

アロー関数はES6(2015年発表)で導入された仕様で無名関数(関数リテラル)の記法です。
従来のfunctionを使った関数と比べると、スマートに書けるのとthisの挙動について迷わなくて済むというメリットがあります。

今までアロー関数を敬遠していたのですが、JavaScriptを解説するサイト(特にReactを使ったサンプル)では結構な頻度でアロー関数を見かけるようになったので、改めて自学のために基本的なポイントを調べてみました。

今までのJavaScriptの関数定義について

従来のfunctionでの定義は下記のように2つの関数定義方法がありました。

//functionを使った関数定義 その1
//通常の関数定義
function myFunc(param) {
  console.log(param);
}

//functionを使った関数定義 その2
//関数リテラルを使った関数定義
let myFunc =  function(param){
  console.log(param);
}

アロー関数はこの「functionを使った関数定義 その2」の関数リテラルを使った定義を簡略化した記述をするためのものとなります。

JavaScriptのアロー関数について

アロー関数はその名の通りアロー記号「=>」を使った関数の定義方式で下記のような記述をします。

//アロー関数を使った関数定義
const hoge = (param) => {
  console.log(param);
}

functionとアロー関数の違いについて

アロー関数を使う理由としては次の2つがあります

1.アロー関数は関数リテラルをシンプルに記述することができる

2.アロー関数はthisの定義を束縛しない

この2つの中で重要なのは2つ目の「thisの定義を束縛しない」という理由のほうです。
まずは、1つ目のシンプルに記述する方法を紹介していきます。

1.アロー関数は関数リテラルをシンプルに記述することができる

アロー関数の基本の書き方

アロー関数の基本はシンプル。下記の構文が基本となります。

(引数,...) => {関数}

アロー関数の基本の書き方のバリエーション

金額をパラメーターで渡したらその金額の消費税を計算する関数をアロー関数で定義する場合

基本的なアロー関数の書き方

//アロー関数の基本構文
(price) => {
  return price * 0.1
}

//上記基本構文を関数として使う場合
let taxCalc = (price) => {
  return price * 0.1
}

console.log('消費税の計算 = ' +taxCalc(800) + '円')

関数内部の本文が1文の場合

本文が1文の場合は関数のブロックであるカッコ「{}」を省略して記述できます。

//1文であればカッコ「{}」を省略できる 
let taxCalc = return (price) => price * 0.1

console.log('消費税の計算 = ' +taxCalc(800) + '円')

本文が1文の場合は関数の内容がリターン値としてみなされるので、「return」の記述も省略できます。

//1文であれば「return」を省略できる 
let taxCalc = (price) => price * 0.1

console.log('消費税の計算 = ' +taxCalc(800) + '円')

関数の引数が一つの場合

引数が一つの場合は引数を囲むカッコ「()」を省略して記述できます。

//引数がひとつであればカッコ「()」を省略できる 
let taxCalc = price => price * 0.1

console.log('消費税の計算 = ' +taxCalc(800) + '円')

確かに色々と省略してコンパクトになっているけどあまりにコンパクト過ぎて逆に分かりにくくなっているような気がするのは僕だけだろうか。。。
アロー関数見慣れていない人がこの「引数が一つの場合」の省略形を見た時に「??」ってなるよなーって思う。

2.アロー関数はthisの定義を束縛しない

アロー関数はthisを束縛しない

「thisを束縛しない??」というのはどういったことなのか??よく意味がわかりませんよね。
アロー関数は自分自身のthisを持たずにアロー関数内のthisは常にグローバルを参照するということなのですが、この意味を理解するのにまずは関数のthisについて解説していきます。

通常の関数のthisスコープについて

以下のようにobj内部の関数からthisを参照した場合のケースで考えてみます。

this.name = 'alice';

const obj = {
  name : 'yogibo',
  myMethod1 : function(){
    console.log(this);
    alert(JSON.stringify(this));
  },
  myMethod2 : function(){
    console.log(this.name);
    alert(this.name);
  },
};

obj.myMethod1(); // {"name": "yogibo"}
obj.myMethod2(); // yogibo 

関数から参照したthisは親オブジェクトobj内部をポインタとして認識しており、obj内部のプロパティにthis経由でアクセスすることができています。
また、グローバルで定義しているthis.name = ‘alice’の参照ができていないことが分かります。

下記は上記のコード例をcodepenで実行できる参考コードです

See the Pen arrow function 1 by Tadashi Sakamoto (@moto3) on CodePen.

通常の関数のthisスコープについて2

次にthisのスコープが変わる例を見てみましょう。

this.name = 'alice';

function outMethod() {
  console.log(this);
  console.log(this.name);
}

const obj = {
  name : 'yogibo',
  myMethod1 : function(){
    console.log(this);
    alert(JSON.stringify(this));
  },
  myMethod2 : outMethod,
};


obj.myMethod1(); // thisはobjを参照。
obj.myMethod2(); // thisはobjを参照。==> 'yogibo'と表示
outMethod();     // thisはWindowを参照。 ==> 'alice'と表示

このコードではobj内部のmyMethod2で呼び出しているoutMethodではobj自身をthisとして参照しているのに対し、outMethodを直接コールした場合はグローバルWindowオブジェクトをthisとして参照しているということがポイントです。
おそらくJavaScriptのthisの混乱の原因となるようなコードがここにあると思われます。

下記は上記のコード例をcodepenで実行できる参考コードです

See the Pen arrow function 2 by Tadashi Sakamoto (@moto3) on CodePen.

myMethod2 をコールした際にthisをグローバル参照のポインタにする場合はbind()メソッドを使用するか、thisを他の変数に代入するのが一般的な対処方法でした。

bind()メソッドを使用する場合

const obj = {
  name : 'yogibo',
  myMethod1 : function(){
    console.log(this);
    alert(JSON.stringify(this));
  },
  myMethod2 : outMethod.bind(this),
};

const obj = {
  name : 'yogibo',
  myMethod1 : function(){
    console.log(this);
    alert(JSON.stringify(this));
  },
  myMethod2 : outMethod.bind(this), // bind()メソッドでthisを参照
};

obj.myMethod1(); // thisはobjを参照。
obj.myMethod2(); // thisはWindowを参照。==> 'alice'と表示
outMethod();     // thisはWindowを参照。 ==> 'alice'と表示

thisを代入する変数を使用する場合

this.name = 'alice';

let that = this;  // 適当な変数(that)を作成してthisを代入
function outMethod() {
  console.log(that);
  console.log(that.name);
}

const obj = {
  name : 'yogibo',
  myMethod1 : function(){
    console.log(this);
    alert(JSON.stringify(this));
  },
  myMethod2 : outMethod,
};

obj.myMethod1(); // thisはobjを参照。
obj.myMethod2(); // thisはWindowを参照。==> 'alice'と表示
outMethod();     // thisはWindowを参照。 ==> 'alice'と表示

アロー関数でthisを参照する場合

this.name = 'alice';

function hello1() { 
  console.log('hello ' + this.name);
}

const obj = {
  name : 'yogibo',
  func : hello1,
  hello2 : function(){console.log("hello " + this.name)},
  hello3 : () => console.log("hello " + this.name)
};

obj.func(); //通常関数1 thisは内部を参照 => hello yogibo
obj.hello2(); //通常関数2 thisは内部を参照 => hello yogibo
obj.hello3(); //アロー関数 thisはグローバルを参照 => hello alice

See the Pen arrow function 3 by Tadashi Sakamoto (@moto3) on CodePen.

アロー関数の使い所

アロー関数は通常の関数の記述の新しい書き方ではないので、今までfunctionで定義していたものをすべてアロー関数に置き換えてしまうと思わぬバグを生むことになります。
アロー関数を使う場合はthisの用途に応じて使ったり、関数リテラルを短くシンプルに記述したい場合等、状況に応じて使い分ける必要がありますね。

参考リンク

MDN Web Docs アロー関数式

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Functions/Arrow_functions

Leave a Reply

Your email address will not be published. Required fields are marked *