JavaScript | 関数で受け取る引数の数を取得する

JavaScript JavaScript
スポンサーリンク

ここからは「関数の定義(fn.length)を解析して、不足している引数を自動で補う」という、メタプログラミングらしい応用例を作っていきましょう。
順を追って、初級 → 実用 → 応用(自動デフォルト注入) の3段階で説明します。


ステップ1:基礎 — 「足りない引数を検出する」

まずは、関数の想定引数(fn.length)と、実際に渡された引数数(args.length)を比べます。

function autoFill(fn) {
  return function(...args) {
    const expected = fn.length;
    const provided = args.length;

    if (provided < expected) {
      console.log(`⚠️ ${fn.name}${expected} 個の引数を期待していますが、${provided} 個しか渡されていません。`);
    }

    return fn(...args);
  };
}

function greet(name, message) {
  return `${message}, ${name}!`;
}

const safeGreet = autoFill(greet);

console.log(safeGreet("Taro")); // ⚠️警告(2つ必要なのに1つしか渡してない)
JavaScript

ステップ2:足りない分を「自動で補う」

ここからがメタっぽい部分です。
引数が足りない場合に「デフォルト値(たとえば null"default")」を補って呼び出します。

function autoFill(fn, defaultValues = []) {
  return function(...args) {
    const expected = fn.length;

    // 足りない引数数を計算
    while (args.length < expected) {
      // defaultValues があれば順番に、それ以外は null で埋める
      args.push(defaultValues[args.length] ?? null);
    }

    return fn(...args);
  };
}

// 使用例
function greet(name, message) {
  return `${message}, ${name}!`;
}

const safeGreet = autoFill(greet, ["Unknown", "Hello"]);

console.log(safeGreet("Taro"));   // → Hello, Taro!
console.log(safeGreet());         // → Hello, Unknown!
JavaScript

🧩 ポイント

  • fn.length が基準。
  • args が足りない分だけ defaultValues から補う。
  • ?? は「undefined または null の場合に右側を使う」演算子。

ステップ3:本格的な「動的デフォルト注入」

上級例では、関数ごとに異なるデフォルト値を「自動で」適用します。
→ 名前や引数数から判断して、関数を動的に補強。

function createSmartWrapper(defaultMap = {}) {
  return function wrap(fn) {
    return function(...args) {
      const expected = fn.length;

      // 足りない引数を自動注入
      while (args.length < expected) {
        const key = `${fn.name}_${args.length}`;
        const def = defaultMap[key] ?? defaultMap["*"] ?? null;
        args.push(typeof def === "function" ? def() : def);
      }

      return fn(...args);
    };
  };
}

// デフォルトマップ
const defaults = {
  "greet_0": "Guest",        // greet の最初の引数が足りないとき
  "greet_1": "Hello",        // greet の2番目が足りないとき
  "*": "N/A"                 // それ以外は共通で N/A
};

// ラッパー生成
const wrap = createSmartWrapper(defaults);

function greet(name, message) {
  return `${message}, ${name}!`;
}

const autoGreet = wrap(greet);

console.log(autoGreet("Taro", "Hi")); // → Hi, Taro!
console.log(autoGreet("Taro"));       // → Hello, Taro!
console.log(autoGreet());             // → Hello, Guest!
JavaScript

🧩 解説

  • defaultMap に「どの関数・どの位置」にどんな値を注入するかを定義。
  • fn.lengthargs.length をもとに、自動で補う。
  • "*" キーで共通のデフォルトを設定できる。
  • デフォルト値を関数として指定すれば、動的な値も注入可能:
const defaults = {
  "log_0": () => new Date().toISOString(),
  "*": "N/A"
};

function log(timestamp, message) {
  console.log(`[${timestamp}] ${message}`);
}

const autoLog = createSmartWrapper(defaults)(log);

autoLog("Hello!");  // timestamp 自動注入 → [2025-10-30T12:34:56.789Z] Hello!
JavaScript

応用パターン:全関数を一括ラップする

この手法を使えば、ライブラリ内のすべての関数に共通のデフォルト値注入機能を追加できます。

const utils = {
  add(a, b) { return a + b; },
  greet(name, msg) { return `${msg}, ${name}`; }
};

const wrapAll = (obj, defaults) => {
  const wrapped = {};
  const wrap = createSmartWrapper(defaults);
  for (const [k, fn] of Object.entries(obj)) {
    wrapped[k] = wrap(fn);
  }
  return wrapped;
};

const enhanced = wrapAll(utils, {
  "add_0": 0,
  "add_1": 0,
  "greet_0": "Unknown",
  "greet_1": "Hi"
});

console.log(enhanced.add(5));      // → 5 (b=0補完)
console.log(enhanced.greet());     // → Hi, Unknown
JavaScript

まとめ:function.length × メタプログラミングの威力

概念意味応用例
fn.length定義時の引数数足りない引数検出・自動補完
args.length実際に渡された数呼び出し時チェック
メタラップ関数関数を動的に加工デフォルト注入・バリデーション・トレース
タイトルとURLをコピーしました