関数の書き方を使い分ける — JavaScript Tips
関数の書き方は主に3つです。宣言・関数式・アロー関数。見た目は近いですが、巻き上げや this、名前の付き方が違います。デフォルト引数と残余引数(rest)も含みます。this の詳細は後回しです。
参考: MDN — 関数 · MDN — アロー関数 · MDN — 残余引数
目次
3つの書き方の概要
// 1. 関数宣言function add(a, b) { return a + b;}// 2. 関数式const multiply = function (a, b) { return a * b;};// 3. アロー関数const divide = (a, b) => a / b;| 形式 | 名前 | 巻き上げ | 典型的な用途 |
|---|---|---|---|
| 関数宣言 | 自動で付く | される | ファイルのメイン処理 |
const / let の関数式 |
変数に依存 | バインディングは作られるが、宣言までは TDZ | コールバック、条件付き定義 |
const / let のアロー関数 |
変数に依存 | バインディングは作られるが、宣言までは TDZ | 短い処理、配列メソッド |
flowchart TD Q{"名前が必要?長い?"} Q -->|トップレベルの処理| D["関数宣言"] Q -->|変数に代入| E["関数式"] Q -->|1行で済む| A["アロー関数"]関数宣言
function greet(name) { return `こんにちは、${name} さん`;}console.log(greet("太郎"));関数宣言は同じスコープ内で 宣言より前に呼び出せます(巻き上げ)。
sayHi(); // OKfunction sayHi() { console.log("Hi");}ファイルやブロックの「エントリ」に近い処理を名前付きで置くときに向いています。
関数式
変数に関数を代入する形です。宣言前には使えません。
// notYet(); // ReferenceError(TDZ のため宣言前は参照できない)const notYet = function () { console.log("後から定義");};notYet(); // OK名前付き関数式(デバッグ用に名前が付く)
const factorial = function fact(n) { if (n <= 1) return 1; return n * fact(n - 1);};条件によって関数を差し替えるときも関数式が扱いやすいです。
let logger;if (process.env.DEBUG) { logger = (msg) => console.log("[DEBUG]", msg);} else { logger = () => {};}(process は Node の例です。ブラウザでは別のフラグで同様のパターンを使います。)
アロー関数
構文が短く、単一式なら {} と return を省略できます。
const double = (n) => n * 2;const sum = (a, b) => { const total = a + b; return total;};const noop = () => { console.log("副作用だけ");};配列メソッドとの相性
const nums = [1, 2, 3];const doubled = nums.map((n) => n * 2);console.log(doubled); // [2, 4, 6]オブジェクトを返すときは括弧で包む
const makePoint = (x, y) => ({ x, y });=> { x, y } だとブロックと解釈され、意図とずれます。
アロー関数にしない方がよい例(概要)
- メソッドとして
thisを使うオブジェクト(通常のfunctionを検討) argumentsオブジェクトに頼る古い書き方(rest を使う)
コールバックや純粋な変換処理ではアロー関数が読みやすいことが多いです。
デフォルト引数
引数が渡されなかったとき(undefined)の既定値を書けます。
function createUser(name, role = "guest") { return { name, role };}console.log(createUser("花子"));// { name: "花子", role: "guest" }console.log(createUser("太郎", "admin"));// { name: "太郎", role: "admin" }式も既定値にできる
function loadConfig(path = defaultPath()) { return path;}function defaultPath() { return "./config.json";}null を渡すとデフォルトは効かない
function pad(value = " ") { return `[${value}]`;}pad(); // "[ ]"(undefined → デフォルト)pad(null); // "[null]"pad(""); // "[]"「未指定」と「空文字」を区別したいときは、呼び出し側の意図をコメントや型で明確にします。
残余引数(rest)
可変個の引数を 配列としてまとめて受け取ります。
function sumAll(...numbers) { let total = 0; for (const n of numbers) { total += n; } return total;}console.log(sumAll(1, 2, 3)); // 6通常引数と組み合わせ
function log(level, ...messages) { for (const msg of messages) { console.log(`[${level}]`, msg); }}log("INFO", "起動", "準備完了");rest は 最後の引数 にだけ置けます。
// function bad(...rest, last) {} // SyntaxErrorスプレッドとの対比
| 構文 | 場所 | 意味 |
|---|---|---|
...args(rest) |
引数リスト | 複数引数 → 配列にまとめる |
...arr(spread) |
配列・オブジェクト | 配列を展開する |
const base = [1, 2];const extended = [...base, 3, 4]; // [1, 2, 3, 4]使い分けの目安
| シーン | 推奨 |
|---|---|
| ファイルのメイン関数、ユーティリティの名前付き API | 関数宣言 |
コールバック、map / filter |
アロー関数 |
| 再帰で内部名が欲しい | 名前付き関数式 |
| 引数省略を許したい | デフォルト引数 |
| 個数不定の引数 | rest |
// 読みやすい組み合わせ例function formatLines(prefix = "", ...lines) { return lines.map((line) => `${prefix}${line}`);}console.log(formatLines("> ", "一行目", "二行目"));// ["> 一行目", "> 二行目"]一行で済むならアロー、複数ステップならブロック付きアローまたは function で足ります。細かい好みより、プロジェクト内で揃える方が効きます。
詰まりやすい点
| 症状 | 原因 | 対処 |
|---|---|---|
| 宣言前に呼べない | 関数式やアローを先に呼んでいる | 宣言を上へ、または関数宣言にする |
=> { key: value } がおかしい |
ブロックと解釈された | => ({ key: value }) |
| デフォルトが効かない | null や 0 を渡している |
デフォルトは undefined のみ。明示的に分岐 |
... で SyntaxError |
rest の位置が最後でない | rest を引数リストの末尾へ |
アローで this が想定と違う |
アローは自身の this を持たない |
メソッドは通常の function を使う |
関連: MDN — デフォルト引数 · MDN — スプレッド構文