概要
関数とは「処理のまとまり」ではない。
それは**“意味の定義、責務の抽出、再利用の契約”**である。
JavaScriptは柔軟な言語であるがゆえに、関数が無秩序になりやすい。
この柔軟さを制御し、壊れず・読めて・使える関数を設計するには戦略が必要である。
本稿では、副作用の管理、引数と戻り値の構造、関数粒度の分離、テスト性と再利用性の担保まで、関数設計の実践原則を体系的に解説する。
1. 副作用のない「純粋関数」が基本原則
function sum(a, b) {
return a + b;
}
- ✅ 副作用なし
- ✅ 同じ入力 → 必ず同じ出力
- ✅ テストが容易 / 予測可能
2. 副作用は外部で注入・分離する
function saveData(data, saveFn) {
return saveFn(data);
}
- ✅ 副作用(保存・API通信など)は関数の外から依存注入
- ✅ 副作用 = I/O / DOM操作 / ネットワーク / 日付取得 / Math.random
3. 引数設計は「構造と意味」で整理する
❌ 曖昧な引数列
createUser('Toto', 25, true);
✅ 意図が明確なオブジェクト引数
createUser({ name: 'Toto', age: 25, isActive: true });
- ✅ 順番のバグ防止 / 意図の明示 / 拡張性あり
4. 戻り値は構造で返し、例外は明示的に扱う
function parseJSON(str) {
try {
return { ok: true, data: JSON.parse(str) };
} catch (e) {
return { ok: false, error: e };
}
}
- ✅ 失敗も含めて戻り値の構造に含める
- ✅ 例外をthrowせず、UI層で明示的に分岐可能
5. 高階関数で共通処理を抽象化
function withLogging(fn) {
return (...args) => {
console.log('call', fn.name);
return fn(...args);
};
}
const loggedSum = withLogging(sum);
- ✅ 関数を関数として扱う設計(ファーストクラス関数)
- ✅ ロジックの共通化 + クロスカッティング処理に有効
6. 関数粒度と命名設計
- ✅ 関数は**「1つの責任」だけを持つ**
- ✅ 長くなる場合は内部処理を小関数に分割
- ✅ 命名は「動詞 + 意図」
fetchUser → getUserDataFromAPI
calculate → calculateFinalScore
設計判断フロー
① この関数に副作用はあるか? → 分離 or 注入可能にする
② 同じ入力で出力が変わるか? → 純粋性を見直す
③ 引数が多すぎないか? → オブジェクト化して構造化
④ 失敗時の戻り値が曖昧では? → エラー構造を含める
⑤ 関数名に責務が表れているか? → 動詞 + 意図ベースに再設計
よくあるミスと対策
❌ 関数内でDOM操作・API呼び出し・ロジックを全部含めている
→ ✅ 副作用・ロジック・表示を分離し、責務を限定する
❌ 引数の順番を間違えてバグ発生
→ ✅ オブジェクト引数に変えて安全性と意図を明示
❌ テストで毎回APIを叩いてしまう
→ ✅ API呼び出しは引数として注入可能にしてテスト分離
結語
関数とは「やること」ではない。
それは**“何を渡せば何が返ってくるか”を明示する契約構造**である。
- 副作用は外に出し
- 引数と戻り値に意味を持たせ
- 小さく、予測可能に、再利用可能に
JavaScriptにおける関数設計とは、
“拡張と修正に強く、テスト可能で、安全な構造の最小単位”である。