LIGデザイナー採用
LIGデザイナー採用
2016.06.02
#6
ES6のある星に生まれて

ES6の新機能「Arrow Function」を使って、よりシンプルなコードを書こう

店長

こんにちは、フロントエンドの店長です。
今回は、ES6の新機能「Arrow Function」についてご紹介します。

Arrow Functionとは、無名関数をよりシンプルな形で書くことができる記法です。ただし、普段使っている関数と挙動が異なるので注意が必要です。うまく使えば、普段のコードがよりすっきりと書けるので、しっかりと特徴を覚えましょう。

無名関数を短く書ける

無名関数とは名前のとおり、自身で名前を持っていない関数を指します。

//名前をつけた関数
function inclement(x) {
    return x + 1;
}

//無名関数
var inclement = function (x){
    return x + 1;
}

こちらがその例です。

Arrow Functionで書いてみよう

先ほど書いたコードを、Arrow Functionで書くと……

var inclement = (x) => {
    return x + 1;
}

このようになります。

関数定義時のfunctionという記述が消え、代わりに=>がでてきました。Arrow Functionは=>が矢印のように見えることから、そのように呼ばれています。

引数は=>の前に記述します。これがArrow Functionの基本の形です。

さらに省略してみよう

先ほどの記述ですが、さらに省略できます。

//さきほどの関数
var inclement = (x) => {
    return x + 1;
}

//引数が1つの場合はカッコを省略可
var inclement = x => {
    return x + 1;
}

//return文のみの場合は波カッコとreturnを省略可
var inclement = x => x + 1;

引数が1つの場合は、カッコを省略できます。さらに、関数内の処理がreturn文しかない場合は、波カッコとreturnを省略できます。

 
ただし、下記の場合は注意が必要です。

/**
* 引数がない場合は、丸カッコの省略はできません
*/
//NG
var say = => {
    return console.log('Hello');
}

//OK
var say = () => {
    return console.log('Hello');
}

/**
* returnを省略した形でオブジェクトリテラルを返す場合は丸カッコで囲む必要があります。
*/
//NG
var getData = () => {value: 1}

//OK
var getData = () => ({value: 1})

引数が1つも存在しない場合は、丸カッコを省略して書くことができません。また、returnで返す値がオブジェクトリテラルの場合は、丸カッコで囲う必要があります。前者はエラーが出力され、後者はundefinedが返ってきます。注意しましょう。

thisを拘束できる

Arrow Functionを使う際に気をつけなければならないのが、通常の関数とArrow Functionではthisの扱いが違う点です。ここをおさえておくと、Arrow Functionをより便利に使うことができるので、しっかりと覚えましょう。

thisの扱いについて

まず知っておかなければならないのが、関数を呼び出した際のthisの扱いについて。JavaScriptでは関数の呼び出し元によってthisの参照先が変わります。4つの例を見てみましょう。

コンストラクタ呼び出し

var Cat = function(){
    this.say = 'meow';
    console.log(this);
}

var cat = new Cat();

thisの参照先は、コンスラクタから生成されたインスタンスを指します。

メソッド呼び出し

var dog = {
    say: 'bow-wow',
    bark: function(){
        return this.say; //bow-wow
    }
}

dog.bark();

thisの参照先は、レシーバオブジェクト(メソッドを呼び出したオブジェクト)、今回の場合dogを参照します。

applyまたはcall呼び出し

var bird = {
    say: 'chirp chirp',
    sing: function(){
        console.log(this.say);
    }
}

var crow = {
    say: 'caw'
}

bird.sing() //chirp chirp
bird.sing.call(crow) //caw
bird.sing.apply(crow) //caw

call()、apply()を使用して呼び出した際は、引数で渡したオブジェクトをthisとして参照します。

その他の呼び出し

function check(){
    return console.log(this);
    this.value = 'test';
};

それ以外の場合は、thisの参照先はグローバルオブジェクト(ブラウザで実行した場合、thisはWindowオブジェクト)を指します。ですから、valueはグローバル変数として扱われます。

引数でthisを保持する

では、下記の場合はどうなるでしょうか。

var name = 'seito';

var person = {
    name: 'tencho',
    say: function(){
        console.log('Hello! I am ' + this.name); //*1

        var say = function (){
            console.log('Hello! I am ' + this.name); //*2
        }

        say(); //*2 Hello! I am seito
    }
}

person.say(); //*1 Hello! I am tencho

結果は、*1は所属しているオブジェクトのthis、*2はグローバルオブジェクトを参照します。これは*1はメソッド呼び出し、*2は関数呼び出しとして扱われるためです。このように、JavaScriptでは呼び出し方によってthisの扱いが変わります。

メソッド内の関数でもオブジェクトのthisを使うためJavaScriptではしばしば、下記のような書き方で回避します。

var name = 'seito';

var person = {
    name: 'tencho',
    say: function(){
        var self = this;
        console.log('Hello! I am ' + self.name); //*1

        var say = function (){
            console.log('Hello! I am ' + self.name); //*2
        }

        say(); //*2 Hello! I am tencho
    }
}

person.say(); //*1 Hello! I am tencho

thisを変数selfに格納し、関数内でselfを参照することで、personを参照できるようにするのです。しかし、この場合、毎回変数にthisを格納しなければいけないため、少し不便に感じます。

Arrow Functionでthisを拘束する

ここで便利なのが、Arrow Functionです。

var name = 'seito';

var person = {
    name: 'tencho',
    say:function() {
        console.log('Hello! I am ' + this.name); //*1

        var say = () => {
            console.log('Hello! I am ' + this.name); //*2
        }

        say(); //*2 Hello! I am tencho
    }
}

person.say(); //*1 Hello! I am tencho

この場合、Arrow Function内のthisは所属しているオブジェクト、今回の場合はpersonを参照します。Arrow Functionは、定義した時点でスコープ内のthisを拘束する特性があります。

 
もう1つ、例を挙げたいと思います。次はコンストラクタから呼び出された場合です。

var Timer = function(){
    this.count = 0;

    var self = this;
    setInterval(function(){
        self.count++;
        return console.log(self.count);
    }, 1000);
}

var timer = new Timer();

コンストラクタから呼び出された場合のthisは、新しく生成されたインスタンスを参照します。しかし、setInterval内のthisは関数実行のため、グローバルオブジェクトを指します。そのため、変数selfにthisを格納して回避しています。

しかし、Arrow Functionを使うと下記のように書くことができます。

var Timer = function(){
    this.count = 0;

    setInterval(() => {
        this.count++;
        return console.log(this.count);
    }, 1000);
}

var timer = new Timer();

変数にthisを格納したのが省け、setInterval内のArrow Functionのthisは、新しく生成されたインスタンスを参照します。毎回thisを変数に入れなければいけなかった手間が省けるので、このようなときは、積極的にArrow Functionを使いましょう。

ただし、jQueryにはthisが特別な値として扱われる場合があります。.on()や.each()などを使用する際にArrow Functionを使用すると、外のオブジェクトをthisとして扱うので、必要に応じて通常の記法とArrow Functionを使い分けましょう。

その他の特徴

その他にもいくつか特徴があるので、説明します。

コンストラクタとしての使用は不可

いままで関数はコンストラクタとして利用できました。しかし、Arrow Functionはコンストラクタとして定義することができません。今まで通りfunctionを使って書く必要があります。

//OK
var Timer = function(){
    this.count = 0;

    setInterval(() => {
        this.count++;
        return console.log(this.count);
    }, 1000);
}

var timer = new Timer();

//NG
var Timer = () => {
    this.count = 0;

    setInterval(() => {
        this.count++;
        return console.log(this.count);
    }, 1000);
}

var timer = new Timer();

//Very Good!!!
class Timer {
    constructor(){
        this.count = 0;
        this.init();
    }
    init(){
        setInterval(() => {
            this.count++;
            return console.log(this.count);
        }, 1000);
    }
}

var timer = new Timer();

ただES6を使うのであれば、関数ではなくクラスでコンストラクタを定義する方法が一番よいでしょう。

クラスについては、下記の記事でご紹介しているのでぜひご覧ください。

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

argumentsオブジェクトとは、関数内で使えるローカル変数です。引数の値を参照することができます。下記がその例です。

var arguments = [4, 5, 6];

var sum = function(){
    var result = 0;
    for(var i = 0; i < arguments.length; i++){
       result += arguments[i];
   }
    console.log(result); //6
}

sum(1, 2, 3);

この場合、引数の合計値の6が結果として出力されます。

しかし、Arrow Functionはargumentsオブジェクトを利用することができません。argumentsは通常の変数の呼び出しと解釈されます。こちらが、その例です。

var arguments = [4, 5, 6];

var sum = () => {
    var result = 0;
    for(var i = 0; i < arguments.length; i++){
       result += arguments[i];
   }
    console.log(result); //15
}

sum(1, 2, 3);

Arrow Function内では外の変数argumentsを参照したため、15という値が出力されました。通常の関数内で、Arrow Functionを使用した場合は、Arrow Functionの外側のargumentsオブジェクトを拘束します。

 

var arguments = [4, 5, 6];

var sum = function (){
    var sum = () => {
       var result = 0;
       for(var i = 0; i < arguments.length; i++){
           result += arguments[i];
       }
        console.log(result); //6
    };
    sum();
}

sum(1, 2, 3);

Arrow Function内ではargumentsを扱うことはできませんが、その代わりとして残余引数を使うことで、argumentsのような振る舞いを実現できます。

残余引数を使う

最後の引数の前に...をつけることによって、複数の値を配列として取得できる引数として利用できます。

var sum =(...arg) => {
    var result = 0;
    for(var i = 0; i < arg.length; i++){
       result += arg[i];
   }
    console.log(result);
}

sum(1, 2, 3);

上記の場合は引数が1つしかないので、argに複数の引数の値が入る形になります。残余引数は通常の関数でも使用できますが、これはES6から登場した記述方法ですので注意してください。

まとめ

Arrow Functionは、使い方さえわかっていれば、コードをよりすっきり見やすくできる便利な記法です。ぜひ、みなさん積極的につかってみてください!

次回はPromiseについてご紹介します。それでは!