ReactのためのJavaScript – アロー関数とthis


はじめに

Reactを学ぶためのモダンなJavaScriptについてまとめます。

今回はECMAScript2015(ES6)で導入されたアロー関数とそのthisの挙動についてまとめます。


アロー関数って?

アロー関数は、ES6から導入された通常の関数(function)を代替する構文です。

//通常の関数
function addOne(n) {
    return n + 1; 
}
addOne(5); //=> 6

//アロー関数1
const addOne = (n) => { return n + 1; };
addOne(5); //=> 6

//アロー関数2
const addOne = n => n + 1;
addOne(5); //=> 6


アロー関数は、functionの記述を省略でき、また関数の中身が今回のように一文の場合はアロー関数2のようにreturnや{...}も省略できます。

アロー関数では、↑の例のように変数に対応付けてその変数名を関数の名前として利用します。このように値を変数に対応付けることをバインドする、または束縛するといいます。


thisについて

実行結果が同じなので通常の関数とアロー関数は全く同じ挙動に見えます。が、thisに関しては挙動が異なります。次のコードを実行してみてください。

this.a = 'global';

//通常の関数
function normalFun() {
    console.log(this.a);
}

//アロー関数
const arrowFun = () => console.log(this.a);

obj = {
    a: 'local',
    normalFun: normalFun,
    arrowFun: arrowFun
};


obj.normalFun(); //=> local
obj.arrowFun(); //=> global

通常関数とアロー関数で出力結果が異なります。

通常関数のthisは呼び出し元のオブジェクトを参照しますが、アロー関数のthisは宣言元のオブジェクトを参照するのです。

thisの問題点

そもそもJavaScriptにおいて、thisの振る舞いはその呼び出し方によって異なります。メソッド呼び出しと関数呼び出しを例にします(* ブラウザで実行)。

メソッド呼び出し

const obj = {
    a: 'method call',
    methodA: function() {
        console.log(this);
    },
    methodB: function() {
        console.log(this.a);
    }
}
//thisはobjを参照
obj.methodA(); //=> {a: "method call", methodA: ƒ}

//thisはobjを参照しているので、this.aはobjのaプロパティにアクセスする
obj.methodB(); //=> method call

関数呼び出し

function fun() {
    this.a = 'function call',
    console.log(this);
}

fun(); //=> Window {parent: Window, opener: null, top: Window, length: 4, frames: Window, …}

関数呼び出しの場合、thisはグローバルオブジェクトを参照します。今回の場合はブラウザで実行しているのでwindowオブジェクトになります(nodeならglobalオブジェクト)。

つまり、通常の関数ではthisが何を差すかは定義した時点では分からず、実行時に決まります。これを把握せずにコーディングしてしまうと↓のような意図しない挙動を生むことにつながります。

const person = {
    name: 'Tom',
    age: 25,
    printName: function() {
        //thisのメソッド呼び出し
        console.log(this.name); //=> Tom
        
        const printAge = function() {
            //thisの関数呼び出し
            console.log(this.age); //=> undefined
        }
        printAge();
    }
};

person.printName(); 
//=> Tom
//=> undefined

この問題に対し、ES6以前では↓のように対処していました。

//対処1: bindでthisを束縛
const person = {
    name: 'Tom',
    age: 25,
    printName: function() {
        console.log(this.name);
        
        const printAge = function() {
            console.log(this.age);
        }.bind(this);
        printAge();
    }
};

person.printName(); 
//=> Tom
//=> 25

//対処2: 変数にthisを代入
const person = {
    name: 'Tom',
    age: 25,
    printName: function() {
        console.log(this.name);
        //thisを変数に代入
        self = this;
        const printAge = function() {
            console.log(self.age);
        }
        printAge();
    }
};

person.printName(); 
//=> Tom
//=> 25

めんどくさいですね。この問題を根本から解決するために導入されたのがアロー関数です。アロー関数で書くとこうなります。

const person = {
    name: 'Tom',
    age: 25,
    printName: function() {
        console.log(this.name); //=>Tom
        
        const printAge = () => {
            //定義元のperson.ageを参照
            console.log(this.age); //=>25
        }
        printAge();
    }
};

person.printName(); 
//=> Tom
//=> 25


まとめ

今回の内容をまとめです。

アロー関数を使うと

  • functionやreturn文の省略が可能
  • thisの値を関数定義時に決めることができる

ここまでご覧頂きありがとうございました。

こちらで他のテーマも扱ってるのでよかったらぜひ!