JavaScript/TypeScript

関数の書き方を使い分ける

4
  • JavaScript

関数の書き方を使い分ける — JavaScript Tips

関数の書き方は主に3つです。宣言関数式アロー関数。見た目は近いですが、巻き上げや this、名前の付き方が違います。デフォルト引数と残余引数(rest)も含みます。this の詳細は後回しです。

参考: MDN — 関数 · MDN — アロー関数 · MDN — 残余引数


目次

  1. 3つの書き方の概要
  2. 関数宣言
  3. 関数式
  4. アロー関数
  5. デフォルト引数
  6. 残余引数(rest)
  7. 使い分けの目安
  8. 詰まりやすい点

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 })
デフォルトが効かない null0 を渡している デフォルトは undefined のみ。明示的に分岐
... で SyntaxError rest の位置が最後でない rest を引数リストの末尾へ
アローで this が想定と違う アローは自身の this を持たない メソッドは通常の function を使う

関連: MDN — デフォルト引数 · MDN — スプレッド構文

シェア