ES2015のアロー関数はthisの扱いが肝でした

このエントリーをはてなブックマークに追加

ES2015のアロー関数はthisの扱いが肝でした

最近はめっきりES2015でJavaScriptを書いているんですが、アロー関数を使い方をちゃんと把握していないことに気づいたので掘り下げてみようかなと思います。

検証環境

Babel · The compiler for writing next generation JavaScript

ローカルに環境作ってもよかったのですが、手っ取り早く確認したかったので、上記でコードの検証しました。

アロー関数は、常に 匿名関数

「アロー関数」は無名関数の省略記法です。なので「関数宣言」では使うことができないです。

ただし全く同じとうわけでは無いので使う上で注意しなければいけない点がある模様です。理由は後述。

では実際に書き方ですが、今までの書き方だとこんな感じでした。

var fn = function(a, b){
  // いろんな処理
}

それをこんな風に書けます。

var fn = (a, b) => {
  // いろんな処理
}

ものすごく基本的な書き方を紹介しました。条件次第ではもっと省略できるようです。その辺についてはアロー関数で確認していただけると。

通常の無名関数との違い

先ほど後述します、とした通常の無名関数と完全に等価というわけではないについて説明します。

  1. thisの扱いが違う
  2. argumentsオブジェクトを持たない
  3. コンストラクタとして振る舞うことはできない

thisの扱いが違う

参考: アロー関数

従来の無名関数とアロー関数とでは関数内部から参照するthisの扱いが異なります。
無名関数は関数の呼び出し方によって関数内のthisの値が変化しますが、アロー関数内部のthisは関数定義時に決定します(レキシカルなthis)。このことは特に重要で注意が必要な点でもあるので次節でもう少し掘り下げます。

レキシカルなthisとな?ちょっと難しいですね。

アロー関数を使うにあたって、特に注意が必要なのは「this」キーワードの存在です。この部分については通常の無名関数とは大きく扱い方が異なっています。

通常の無名関数の場合、関数内の「this」の参照先は、「関数がどのようにして呼ばれたか」によって変化します。 (参考:JavaScriptの「this」は「4種類」?? – Qiita)

しかし、アロー関数では関数定義時のコンテキスト(スコープ)のthisを常に参照するようになります。言い換えると、アロー関数はそれが定義された場所によって関数内部のthisの値が固定されるということです。

要は呼び出し方によって変化していたthisが定義した場所によって値が固定されるよってことなんですが、コードを見た方がわかりやすいです。

以下のコードをBabelのTry it outで確認してみます。

var obj = {
  show: function () {
    console.log(this); //Object

    var funcA = function () {
        console.log(this); //undefined
    };

    funcA();

    var funcB = () => {
        console.log(this); //Object
    };

    funcB();
  }
};

obj.show();

すると従来の書き方ではundefinedでアロー関数ではObjectですね。さらに面白いのが、トランスパイルしたコードを見るとvar _this = this;としていますね。

従来であればvar _this = this;とかvar self = this;としていたのを使わないで済むってことですね。

argumentsオブジェクトを持たない

アロー関数内部ではargumentsオブジェクトにアクセスすることができません。

これは単純に使えないってだけですね。

var fn1 = function (param) {console.log(arguments[0]);};

fn1('hoge'); // hoge

var fn2 = (param) => {console.log(arguments[0])};

fn2('fuga'); // Error: arguments is not defined

コンストラクタとして振る舞うことはできない

アロー関数ではthisの値が関数定義時に決まるため、必然的にコンストラクタとしては使用することができません。

なるほどー。

実際にコードにしてみると

var Person = function (name, age) {
    this.name = name;
    this.age = age;
};

var taro = new Person('Taro', 16);
console.log(taro.age); //16
var Person = (name, age) => {
    this.name = name;
    this.age = age;
};

//コンストラクタとして呼び出した時点でエラーが発生
var taro = new Person('Taro', 16); //Error: not a constructor

ふむふむ。この辺は従来の関数との使い分けが必要になってきそうですね。

注意は必要だけど使って損なし

注意するとこはあれど積極的に使っていっていいなーって思いました。

selfやthatは使わないですみますし、あとbind(this)とかもしなくていいようです。(これだけは覚えておこうES2015(旧ES6)【アロー(=>)関数】
)このような点からもかなり省略した書き方ができますね。

注意する点

やっぱりここはthisの扱いが今までと違う点が挙げられます。分かりにくければアロー関数をブロック文のように見れれば「this」が何を指しているかわかりやすいかもしれません。

あとは従来の「function」との使い分けがポイントになりそうです。

例えば従来の書き方で以下のような書き方であれば期待通りの動きをします。

var obj = {
  name: 'Taku',
  greeting: function() {
    console.log('my name is ' + this.name);
  }
}

obj.greeting();

ですが、これをアロー関数で書いてみると

var obj = {
  name: 'Taku',
  greeting: () => {
    console.log('my name is ' + this.name);
  }
}

obj.greeting();

Cannot read property 'name' of undefinedになります。原因としてはthis がグローバルになってしまうになってしまうからなんですが、強引にアロー関数を使う場合なら

var obj = {
  name: 'Taku',
  greeting: function(){
    (() => {
      console.log('my name is ' + this.name);
    })();
  }
}

obj.greeting()

アロー関数を即時関数として使えば期待通りの動きになります。ただまどろっこしいのでこういった場合は従来の「function」を使った方が良さそうですね。

まとめ

thisのこととかよくわかっていないまま使っていました。こういった感じで一部のことを掘り下げて調べるとその仕様や使い方、注意する点が明確になってちゃんと理解して今後は使えるようになるのでいいですね。今後も掘り下げたテーマをどんどんやっていこうかなーと思います。

参考

オススメの本



初めてのJavaScript 第3版 ―ES2015以降の最新ウェブ開発

ECMAScript2015(ES2015)の入門書。シンプルな例題を多用しブラウザやnodeコマンドで試しながら新しいJavaScriptを楽しく学びます。従来バージョンを使用中のJavaScriptプログラマーにも、これからJavaScriptを習得したい入門者にも有用な情報が満載です。本書を読めば、letやconstによる宣言とスコープ、関数の基本と高度な使い方、オブジェクトとオブジェクト指向プログラミング、イテレータやジェネレータやプロキシといったES2015の新機能、非同期プログラミング、クライアントサイドのDOMやjQuery、サーバーサイドのNode.jsなど、JavaScriptの最新バージョンを使った開発に関する総括的な知識をバランスよく得られます。日本語版では、ES2016およびES2017の新機能の紹介も加えました。

イチマルニデザインブログをフォローしよう

イチマルニデザインブログではTwitterアカウントでWebに関する情報をつぶやいています。フォローすることで最新情報をすぐに受け取ることができます

いいなと思ったらシェアお願いします

このエントリーをはてなブックマークに追加

同じカテゴリーの記事

ページの先頭へ