Step 1: Web Basics
03. アロー関数の書き方:this・return・通常の関数との違い
アロー関数は、短いコールバックや変換処理を表す方法です。通常の関数とthisの扱いが違うため、単にfunctionを短くした記法として置き換えないようにします。猫の手ツールの保存ガイドを開く処理と、小さな独立例で使い分けを確認します。
function って書かないの?
関数といえば function() だと覚えていたのですが、ES6(2015年)以降の最近のJavaScriptでは、 () => という書き方(アロー関数)が頻繁に使われます。
短いコールバックを読み始めるときは、引数・本体・返り値の3つを先に探します。
// 昔の書き方
btn.onclick = function(event) {
console.log("クリックされました");
};
// 今の書き方(アロー関数)
btn.onclick = (event) => {
console.log("クリックされました");
}; さらに、引数が1つだけの場合は ( ) を省略できたり、本体が1つの式である場合は や return すら省略できるという柔軟性も持っています。
短く書けるだけじゃない。最大の理由は「this」
アロー関数はfunctionの単なる省略形ではなく、thisを作らず外側のthisを参照する関数です。
従来の function で起きる「thisの迷子問題」
通常の関数のthisは呼び出し方で変わります。setTimeoutへ渡すコールバックは、オブジェクトのメソッドとして呼び出される場合と区別します。
const obj = {
name: "猫の手ツール",
greet: function() {
// obj.greet() と呼び出した場合、この this は obj を指す
console.log("I am " + this.name);
setTimeout(function() {
// ブラウザでは this は通常 window。obj.name にはならない
console.log("Hello from " + this.name);
}, 1000);
}
}; 昔のJavaScriptエンジニアは、この問題を回避するために const self = this; といった変数をわざわざ作って対処していました。
アロー関数で解決!
アロー関数は「自分が定義された場所の this をそのまま引き継ぐ」という特性を持っています(レキシカルスコープと呼ばれます)。これにより、直感的にコードが書けるようになります。
const obj = {
name: "猫の手ツール",
greet: function() {
setTimeout(() => {
// アロー関数なら、外側の this.name ("猫の手ツール") をそのまま使える!
console.log("Hello from " + this.name);
}, 1000);
}
}; アロー関数を使ってはいけない場面
とても便利なアロー関数ですが、実は何でもかんでもアロー関数にすれば良いわけではありません。以下のような場面では、従来の function を使う必要があります。
- オブジェクトのメソッド定義: 上記の例の
greetのように、オブジェクト自身のプロパティにアクセスしたい場合はfunctionを使います。 - イベントリスナーで this から登録先の要素を取得したい時:
addEventListener内でthisを使ってクリックされた要素(e.currentTarget相当)を取得したい場合は、アロー関数だとthisがズレてしまいます。
呼び出し方と返り値から選ぶ
猫の手ツールの実際のコードでも、配列の操作(map や filter)や、UIのイベント処理にアロー関数を使えます。イベントの登録先は、アロー関数でも event.currentTarget で取得できます。
短い変換処理にはアロー関数、呼び出し元に応じた this が必要な処理には通常の関数、というように役割から選べます。アロー関数は new で呼び出すコンストラクターには使えません。
省略記法で返す値を確認する
式をそのまま返す形と、波括弧で処理を書く形では return の要否が違います。以下はそれぞれ独立した関数です。オブジェクトを直接返す場合は丸括弧で囲みます。
const double = value => value * 2;
const doubleWithBlock = value => { return value * 2; };
const makeSize = width => ({ width, height: width });
console.log(double(3), doubleWithBlock(3), makeSize(200));
// 6 6 { width: 200, height: 200 } 保存ガイドの操作は、閉じた変数を使うコールバック
画像変換ツールでは、取得済みのiphoneSaveModalをアロー関数の中で参照します。呼び出し元のthisを使わず、true/falseを明示的に渡す設計です。
const toggleModal = (show) => {
if (iphoneSaveModal) {
if (show) iphoneSaveModal.classList.remove('hidden');
else iphoneSaveModal.classList.add('hidden');
}
};
if (btnModalOpen) btnModalOpen.onclick = () => toggleModal(true);
if (btnModalClose) btnModalClose.onclick = () => toggleModal(false);これは本番コードから型指定を省いた抜粋です。ボタン自身が必要ならevent.currentTargetを読む方法もあり、「イベントだから通常の関数」と一律に決める必要はありません。制作解説の保存までの流れと、イベントの登録方法を合わせて確認してください。
まとめ
波括弧を付けたのに return を書かなかった場合、返り値は undefined になります。map の結果が空のように見えるときは、まずこの違いを確認します。