LoginSignup
118
94

More than 1 year has passed since last update.

アロー関数と関数の違いとthisの評価のされ方

Last updated at Posted at 2018-05-03

アロー関数と関数の違い

アロー関数(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>
    );
  }
}

参考

118
94
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
118
94