アロー関数と関数の違い
アロー関数(arrow function)はES6から導入された「関数」の新しい書き方。
ではなぜ、わざわざ新しくアロー関数が導入されたのか?
それは、違いは何かという問いと同義であり、大きく分けて理由は二つある。
1. functionより短く書ける
毎回function
と宣言する必要はなく、一行で書くこともできる。
// function
function inc(x) {
return x + 1;
}
// arrow function
const inc = (x) => x + 1; // 出力の式が一行の場合はreturnは不要
2. 宣言元のthisを参照する
thisとは、オブジェクトであり、どのオブジェクトであるかは、関数の呼ばれ方によって決定する。
arrow function と function は内部のthisの参照先のオブジェクトが異なる。
- arrow function: 内部のthisは宣言時のスコープを持つオブジェクトになる
- function: 内部のthisは実行時のレシーバであるオブジェクトになる
this.data = 'this is within global';
function func() {
// 実行時のレシーバであるオブジェクトをthisとして定義する
console.log(this.data);
}
const arrowFunc = () => {
// 宣言時のスコープを持つオブジェクトをthisとして定義する
// ここではグローバルオブジェクト
console.log(this.data);
};
const f = {
data: 'within object',
execute: func
};
f.execute();
// => 'within object'
const af = {
data: 'this is within object',
execute: arrowFunc
};
af.execute();
// => 'this is within global'
上記の二つ以外にも差異はある。
3. arrow functionは変数定義と同様の扱いでファイルの上の行からの解釈が明示的
function 宣言だとこういうのが書けてしまう。
a() // OK
function a() {}
arrow function は定義してから呼び出さないといけない。
4. functionはdefault exportが一行で書ける
逆に default export が1行で書けるという点では function 宣言のほうが優位になる。
default export function a() {} // OK
const b = () => {}
default export b // arrow functionの場合、定義行で同時に default export できない
bind(this)は何をしているのか
thisは関数の呼ばれ方によって異なるが、イベントハンドラのコールバック関数で、
bind(this)
をするのはなぜか?
bindとは、関数内部のthisの参照するオブジェクトを確定してしまう関数のこと。
this.a = 'global';
function x() { console.log(this.a) };
x();
// => 'global'
y = x.bind({ a: 'object' });
y();
// => 'object'
DOMが構築され、イベントによるコールバック関数が実行された時には、thisで呼び出し元のComponentのオブジェクトを参照できなる。(es6の場合)
そのために、bindを使って関数内部のthisとComponentのオブジェクトを結びつける。
import React from 'react';
export default class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = { isToggleOn: true };
// このオブジェクトをイベントハンドラの内部のthisに結びつける
this._handleClick = this.handleClick.bind(this);
}
handleClick() {
// `this`がこのオブジェクトであるという前提になっている
this.setState(prevState => ({
isToggleOn: !prevState.isToggleOn
}));
}
render() {
return (
<div>
{
/*
bind(this)をしているので、'This'はToggle Componentになる。
OnClickイベントはstateを変える
*/
}
<button onClick={this._handleClick}>
{ this.state.isToggleOn ? 'ON' : 'OFF' }
</button>
{
/*
bind(this)をしていないので、'This'はnullになる
OnClickイベントで、以下のエラーになる。
Uncaught TypeError: Cannot read property 'setState' of null
*/
}
<button onClick={this.handleClick}>
{ this.state.isToggleOn ? 'ON' : 'OFF' }
</button>
{
/*
アロー関数は、宣言時のスコープのオブジェクトになるので、'This'はToggle Componentになる
OnClickイベントは、明示的にbind(this)をしなくても、stateを変更できる
しかし、renderのたびに、この関数は定義される、新しい参照をもつ関数ができ、仮想DOMで差分が生じたと判断される
*/
}
<button onClick={() => this.handleClick()}>
{ this.state.isToggleOn ? 'ON' : 'OFF' }
</button>
</div>
);
}
}