読者です 読者をやめる 読者になる 読者になる

つばさのーと

つばさの日常を綴るのーと

JavaScriptのアロー関数のハマりどころについて

f:id:tsubasa123:20170119171032j:plain

 

こんにちは、つばさ(@tsubasa123)です。

 

先日の続き。アロー関数ってやっぱり便利で使えるときは積極的に使うようにしているのですが、まだまだthisに悩まされておりまして。

 

もう少し詳細に把握しておきたかったので色々試した過程をメモしておきます。

 

thisは定義時に束縛される

 

まずはおさらいから。

 

const tsubasa = {
    name: 'tsubasa',
    hello: function() {
        setTimeout(() => {
            console.log('hello ' + this.name);
        });
    },
};
tsubasa.hello(); // tsubasa

 

このように、setTimeout()関数などに渡すコールバックをアロー関数で定義すると、関数内でのthisはグローバルオブジェクトではなく、アロー関数を定義したコンテキストのthisオブジェクトを参照するようになる。

 

先の例だとtsubasaオブジェクトのhelloメソッド内でアロー関数が定義されているので、helloメソッドが参照するthis、つまりtsubasaオブジェクトを参照するようになる、と。

 

アロー関数を定義したコンテキストのthis」って言い回しに違和感を覚えますが、アロー関数を利用することでthisがグローバルオブジェクトに置き換わってしまうJavaScriptの謎仕様から解放されるようになることがわかりました。

 

ここまでは大体把握していました。ここからは実際にアローを使って戸惑ったところをまとめていきます。

 

オブジェクトのメソッドにアロー関数を利用した時

 

先ほどのコードに似ていますが、このように指定するとエラーになります。

 

const tsubasa = {
    name: 'tsubasa',
    hello: () => {
        console.log('hello ' + this.name); // Cannot read property 'name' of undefined
    },
};
tsubasa.hello();

 

helloメソッドの定義をアロー関数に変更すると、このようにエラーになります。アロー関数を定義する時のコンテキストのthisはこの場合はtsubasaではなく、グローバルオブジェクトになってしまうのでプロパティが見つからずにエラーとなります。tsubasaオブジェクトの定義時のthisが束縛されることになるのかな。

 

アロー関数が使えるからといってむやみやたらにコードを書き替えるとこのようなエラーになることもありますので気をつけましょう。私はえらい目にあいました。

 

イベントハンドラにアロー関数を利用した時

 

今回はjQueryを利用してハマりましたが、本質的にはjQueryは関係なく、addEventListenerを利用しても同じことになると思います。id=hogeの要素に何かテキストが入っていると仮定します。

 

$hoge.on('click', function(e) {
    console.log($(this).text()); // 要素の中のテキストが表示されます
});

 

コールバックにアロー関数を使わない従来の指定方法だと、コールバック関数内のthisにはイベントの対象となる要素、この場合はクリックされたDOM要素が参照されるので、上記のようにthisを利用してクリック対象にアクセスすることができます。

 

$hoge.on('click', (e) => {
    console.log($(this).text()); // 何も表示されない
});

 

対して、アロー関数を利用すると要素のテキストは表示されません。理由はもうおわかりですね。thisが参照するオブジェクトがクリックされたDOM要素ではなく、アロー関数を定義する時のコンテキストのthisを参照するからです。

 

アロー関数をイベントハンドラで指定する際は、

 

$hoge.on('click', (e) => {
    console.log($(e.currentTarget).text()); // 要素の中のテキストが表示されます
});

 

このように、イベントオブジェクトを経由してイベント対象の要素にアクセスする必要があります。

 

jQueryへのコールバックにアロー関数を利用した時

 

なんて名前をつければよいのかわからなかったのですが、コードを見てもらえればなんとなくニュアンスは伝わるかと思います。

 

$('#list li').each(function() {
    console.log($(this).text()); // 要素の中のテキストが表示されます
});

 

jQueryで取得したDOM要素に対して何か処理を行いたいときは、eachメソッドなどを使ってコールバックを指定することができます。ここにも当然アロー関数は指定できます。

 

$hoge.on('click', (e) => {
    console.log($(this).text()); // 何も表示されない
});

 

アロー関数をコールバックに利用すると、イベントハンドラの時と同じ結果になってしまいます。

 

$('#list li').each((k, v) => {
    console.log($(v).text());
});

 

アロー関数を利用したときは、コールバック関数の引数を利用して各要素にアクセスするようにします。このとき、引数は2つ渡されてきて2つ目に対象となる要素が格納されているので注意しましょう。1つ目と勘違いすると結構ハマったりします。

 

アロー関数マジ便利

 

const divRegion = {
    name: 'hogehoge',
    init: function() {
        $('#btn').on('click', (e) => {
            console.log(this.name); // hogehoge
            console.log($(e.currentTarget).text()); // 要素の中のテキストが表示されます
        });
    },
};
divRegion.init();

 

当たり前ですが、併用すると捗ります。jQueryを使って、DOMベースでゴリゴリとイベント駆動させるならアロー関数じゃないほうがDOM要素にアクセスしやすいかもしれません。ですが、まぁ、管理は難しくなりますよね。

 

一長一短なところがあると思いますが、アロー関数のthisの特性を把握すればコーディングは捗ること間違いない、と思いました。

 

さいごに

 

だいぶわかってきました、アロー関数。やはり新技術は導入していかないとダメですね、べんりべんり。機会があればトランスパイラ使ってブログのカスタマイズにも導入してみたいところです。

 

ではでは、最後までお付き合いいただきありがとうございました。

 

関連記事

今さらだけどJavaScriptのアロー関数の使い方を調べました