ponday.com

(Qiitaから移植)ES2015のアロー関数におけるthisの取り扱いについて

Development
2016-09-18

本記事は2016年9月19日にQiitaに投稿した記事を移行したものです。

勉強中にハマったのでメモ

アロー関数とは

ES2015で採用された新機能です。無名関数の省略記法みたいな感じ。

'use strict';
// 配列の全要素を2乗した配列を作る
var array = [1, 2, 3];

// ES5まで
console.log(array.map(function (item) { return item * item; })); // -> [1, 4, 9]

// ES2015
console.log(array.map((item) => { return item * item; })); // -> [1, 4, 9]

このように、無名関数と同じ感覚で使うことができます。

アロー関数 == 無名関数ではない

一見、Array.prototype.mapやArray.prototype.filterの引数として使っている分にはただの糖衣構文のように見えます。 しかし、厳密にはアロー関数と無名関数は等価ではありません。

具体的には、定義する関数内でthisを参照する場合に挙動が変化します。 まずはサンプルです。

'use strict';
let Sample = function(func) {
    return {
        array: [1, 2, 3],
        f: func
    };
};

// 無名関数
let funcA = function () { console.log(this.array); }
let objA = new Sample(funcA);
objA.f(); // -> [1, 2, 3]

// アロー関数
let funcB = () => { console.log(this.array); }
var objB = new Sample(funcB);
objB.f(); // -> undefined

どちらもthis.arrayを表示するだけで全く同じ処理ですが、表示結果が異なっています。 なぜこのような動作になるかというと、無名関数とアロー関数ではthisが決定するタイミングが異なるためです。

無名関数の場合

無名関数の場合、thisの値は関数呼び出し時に決定します。 thisの値は、関数が呼び出された時の呼び出し方、呼び出された場所などに応じて動的に変化します。

上記サンプルの場合、objA.f()として呼び出された無名関数におけるthisはobjAを指しますので、this.array == objA.arrayとなり、問題なく表示することができます。

アロー関数の場合

アロー関数の場合、thisの値が関数が定義された時点で決定します。 アロー関数が無名関数と決定的に異なるのはこの部分です。呼び出され方や呼び出された場所には依存しません。

上記サンプルの場合、let funcB = () => { console.log(this.array); }の時点でthisの値が決定します。 this != objBなので、当然this.arrayはundefinedになります。

Babelでトランスパイルしてみると明確で、アロー関数の場合のみトランスパイル時点でconsole.log(undefined.array)のようにthisが対応するオブジェクトに置き換えられています。(undefined.arrayって変換結果はどうかと思いますが・・・)

おわりに

Vue.jsの勉強中にハマったのですが、初めはまさか関数定義の仕方が原因とは思わず、余計な時間を使ってしまいました(情けない;)

ちなみにハマったサンプル(本当にvue.jsとは関係のないところでハマった)↓

import Vue from 'vue/dist/vue';

let App = Vue.extend({
    template: `
        <div>
            <h1>{{ label }} - {{ message }}</h1>
            <button @click="onClick">click</button>
        </div>
    `,
    data: () => {
        return {
            label: 'message is'
        };
    },
    props: {
        message: {
            type: String,
            default: () => { return ''; }
        }
    },
    methods: {
        // ここをonClick: (evt) => { ... }で書くと this == undefined
        onClick: function (evt) {
            console.log(this);
        }
    }
});
export default App;

謝辞

今回以下の記事を参考にさせて頂きました。非常に分かりやすくまとめて下さっていますのでこちらもご参照ください。

JavaScriptの「this」は「4種類」??

#JavaScript

Share

tweetブックマークshare

Profile

アバター

pondayEngineer

フロントエンドもバックエンドもまんべんなく。 TypeScriptが好き。

  • Twitter
  • GitHub
Copyright © 2019 @ponday All Rights Reserved.