概要
JavaScriptでは、同じ“関数”であっても
記述方法によって挙動・スコープ・バインディング・実行タイミングが大きく異なる。
function foo() {}
const foo = function() {}
const foo = () => {}
それぞれの選定を「なんとなく」で済ませていると、thisのバグ、タイミングのズレ、コードの責務の曖昧さが生まれる。
この記事では、関数の3つの記法を構造的に比較し、使い分けるための判断基準を明確にする。
対象環境
ES5〜ES2023対応のJavaScript(Node.js / ブラウザ)
3つの記法とその特徴
書き方 | 名前 | this束縛 | ホイスティング | 再定義 | 用途 |
---|---|---|---|---|---|
function foo() {} |
関数宣言 | 動的 | ✅ あり | ✅ 可能 | 汎用 / ライブラリなど |
const foo = function() {} |
無名関数式 | 動的 | ❌ なし | ❌ 不可 | コールバック / スコープ内関数 |
const foo = () => {} |
アロー関数 | 静的 | ❌ なし | ❌ 不可 | thisが要らない関数 / 簡潔な処理 |
1. 関数宣言
function greet(name) {
return `Hello, ${name}`;
}
✅ 特徴
- ✅ ホイスティングされる → ファイル先頭で定義してなくても使用可能
- ✅ 名前が明示される → デバッグで追いやすい
- ❌
this
は呼び出し元に依存する(動的)
2. 関数式
const greet = function(name) {
return `Hello, ${name}`;
};
✅ 特徴
- ❌ ホイスティングされない
- ✅ 変数スコープ内に閉じることができる
- ✅
this
は関数宣言と同じく動的(ただし限定されやすい)
→ コールバックや関数を値として扱いたいときに便利
3. アロー関数
const greet = (name) => `Hello, ${name}`;
✅ 特徴
- ✅
this
を外側のスコープからレキシカルに継承する - ✅ 簡潔な記法で使い捨てに最適
- ❌
arguments
が使えない - ❌ コンストラクタとして使えない(
new
不可)
→ DOM操作やイベントハンドラでのthisトラブルを避けるための強力なツール
thisに関する設計の分岐点
✅ 通常関数でのthis
const obj = {
name: 'toto',
greet: function () {
console.log(this.name); // 'toto'
}
};
❌ アロー関数では this は外側に束縛される
const obj = {
name: 'toto',
greet: () => {
console.log(this.name); // undefined
}
};
ホイスティングと関数式の違い
greet(); // ✅ OK(関数宣言)
function greet() {
console.log('Hello');
}
greet2(); // ❌ ReferenceError(関数式)
const greet2 = function () {
console.log('Hello');
};
実務的な選定指針
使用目的 | 推奨構文 | 理由 |
---|---|---|
グローバルに公開する関数 | 関数宣言 | ホイスティング + 名前が明示的 |
thisが関係するオブジェクトメソッド | 関数式(function) | thisが動的でもスコープが明確 |
短く簡潔な処理 / コールバック | アロー関数 |
this が不要 + 可読性高 |
コンストラクタ関数 | 関数宣言 | アロー関数では new が使えない |
イベントハンドラ(内部で this 使用) | アロー関数 | DOM外の this に束縛しないため |
よくある誤用
❌ アロー関数でコンストラクタ
const User = (name) => {
this.name = name;
};
const u = new User('toto'); // ❌ TypeError
→ ✅ 通常の関数で書くべき
❌ thisを使いたいのにアロー関数
const obj = {
name: 'toto',
init: () => {
console.log(this.name); // ❌ undefined
}
};
→ ✅ function()
で書く必要がある
結語
関数を選ぶということは、構文を選ぶのではなく、責任の持ち方を選ぶということ。
- this を持たせるか
- スコープに閉じ込めるか
- 再定義やホイスティングを許容するか
構文は表層、設計は本質。
どの関数記法を選ぶかで、コードの性格は180度変わる。
JavaScript関数は「どう書くか」ではなく、「どう使われるか」で設計すべきである。