アロー関数
このトピックを終えると
アロー関数の構文を自由に使えるようになり、通常の関数との主な違い(thisのバインディング)を説明でき、アロー関数を使うべき場合と避けるべき場合を判断できるようになります。
functionキーワードからアロー関数へ
ES6以前は、関数を作成するときは常にfunctionキーワードを使っていました。
const add = function(a, b) {
return a + b;
};アロー関数に置き換えると:
const add = (a, b) => {
return a + b;
};本体が単一の式である場合、中括弧とreturnを省略できます。
const add = (a, b) => a + b;引数が1つだけの場合、括弧も省略できます。
const double = x => x * 2;構文バリエーションまとめ
// 引数なし — 括弧は必須
const greet = () => "こんにちは";
// 引数1つ — 括弧は省略可能
const square = x => x * x;
// 引数2つ以上 — 括弧は必須
const sum = (a, b) => a + b;
// 本体が複数行 — 中括弧 + returnは必須
const calculate = (a, b) => {
const result = a * b;
console.log(result);
return result;
};
// オブジェクトを返す場合 — 括弧で囲む
const makeUser = (name) => ({ name: name, role: "user" });最後のパターンは、しばしば混乱を招きます。{ name: name }をそのまま書くと、JavaScriptは関数の本体の中括弧として解釈します。括弧()で囲むことで、「これはオブジェクトリテラルだ」と伝える必要があります。
コールバックで輝くアロー関数
アロー関数は、配列メソッドのコールバックとして使用するときに最も自然です。
const numbers = [1, 2, 3, 4, 5];
// 通常の関数
const doubled = numbers.map(function(n) {
return n * 2;
});
// アロー関数
const doubled = numbers.map(n => n * 2);filter、reduce、forEach、sortなどの配列メソッドで毎回functionキーワードを使用すると、コードが長くなり、主要なロジックが隠れてしまいます。アロー関数が採用された最大の理由の一つです。
// 実務パターン:チェーン
const result = users
.filter(u => u.age >= 18)
.map(u => u.name)
.sort((a, b) => a.localeCompare(b));this — 最も重要な違い
通常の関数のthisは、呼び出し方法によって決まります。アロー関数のthisは、宣言された位置で決まります。
const timer = {
seconds: 0,
// ❌ 通常の関数 — `this`はwindowを指す
startBroken: function() {
setInterval(function() {
this.seconds++; // `this` === window (または strictモードではundefined)
console.log(this.seconds); // NaN
}, 1000);
},
// ✅ アロー関数 — `this`はtimerオブジェクトを指す
start: function() {
setInterval(() => {
this.seconds++; // `this` === timer
console.log(this.seconds); // 1, 2, 3...
}, 1000);
}
};setIntervalに通常の関数を渡すと、その関数が実行されるときにthisはグローバルオブジェクト(window)になります。コールバックのthisが意図と異なるというのは、JavaScriptの最大の落とし穴の一つでした。
アロー関数は、独自のthisを作成しません。代わりに、宣言された外側のスコープのthisをそのまま使用します。これを**レキシカルthis**と呼びます。
ES6以前は、この問題を回避するためにvar self = this;や.bind(this)を使用していました。
// ES5時代の回避策
start: function() {
var self = this;
setInterval(function() {
self.seconds++;
}, 1000);
}アロー関数はこのパターンを完全に置き換えました。
アロー関数を使うべきではない場合
1. オブジェクトのメソッド
const user = {
name: "鉄郎",
// ❌ アロー関数 — `this`はuserを指さない
greet: () => {
console.log(`こんにちは、${this.name}`); // undefined
},
// ✅ 通常の関数(短縮構文) — `this`はuserを指す
greet() {
console.log(`こんにちは、${this.name}`); // "こんにちは、鉄郎"
}
};オブジェクトリテラルのメソッドとしてアロー関数を使用すると、thisはオブジェクトではなく、外側のスコープ(通常はグローバル)を指します。
2. コンストラクタ
// ❌ アロー関数でコンストラクタは不可
const Person = (name) => { this.name = name; };
new Person("鉄郎"); // TypeError: Person is not a constructorアロー関数にはprototypeがないため、newキーワードを使用できません。
3. DOMイベントハンドラでthisが必要な場合
// ❌ アロー関数 — `this`はボタンを指さない
button.addEventListener("click", () => {
this.classList.toggle("active"); // `this` !== button
});
// ✅ 通常の関数 — `this`はイベントの対象要素を指す
button.addEventListener("click", function() {
this.classList.toggle("active"); // `this` === button
});ただし、event.targetまたはevent.currentTargetで代替できるため、アロー関数を使用しても問題ない場合があります。
アロー関数にないもの
| 機能 | 通常の関数 | アロー関数 |
|---|---|---|
独自のthis | ✅ | ❌(レキシカル) |
argumentsオブジェクト | ✅ | ❌ |
new呼び出し(コンストラクタ) | ✅ | ❌ |
prototype | ✅ | ❌ |
super | ✅ | ❌(レキシカル) |
argumentsがない代わりに、残りの引数(...args)を使用します。
// 通常の関数
function sum() {
return [...arguments].reduce((a, b) => a + b, 0);
}
// アロー関数
const sum = (...args) => args.reduce((a, b) => a + b, 0);...argsはargumentsよりも明示的で、配列メソッドをすぐに使用できるため、むしろアロー関数の方法が推奨されます。
判断基準
コールバック関数か? → アロー関数
配列メソッド(map/filter/など)か? → アロー関数
オブジェクトのメソッドか? → 通常の関数(短縮構文)
コンストラクタが必要か? → 通常の関数(またはclass)
`this`を動的に変更する必要があるか? → 通常の関数キーとなる一文: アロー関数は
() => {}で記述し、外側のスコープのthisをそのまま使用します(レキシカルthis)。コールバックにはアロー関数、オブジェクトのメソッドには通常の関数 — これだけ覚えておけば十分です。