JavaScript Tips | 基本・共通ユーティリティ:型チェック – 関数判定

JavaScript JavaScript
スポンサーリンク

関数判定とは何を見分けたいのか

「関数判定」は、その値が「本当に関数かどうか」を見分けることです。
業務コードでは、「コールバックを受け取る」「オプションでフック関数を渡せる」「イベントハンドラを登録する」など、
「渡された値が関数なら呼ぶ、関数でなければスルーする」という場面がとても多くあります。

ここで関数かどうかをきちんと判定できないと、value() のように呼び出した瞬間に
TypeError: value is not a function で落ちてしまいます。
だからこそ、「関数判定」は地味ですが、実務ではかなり重要なユーティリティになります。


JavaScript における「関数」の基本

JavaScript では、関数も「値」です。
変数に入れたり、引数として渡したり、戻り値として返したりできます。

function greet() {
  console.log("こんにちは");
}

const sayHello = greet;      // 関数を変数に代入
const run = (fn) => fn();    // 関数を引数として受け取る

run(sayHello);               // "こんにちは"
JavaScript

この「関数も値として扱える」という性質のおかげで、柔軟な設計ができますが、
同時に「本当に関数が渡ってきているか?」をチェックする必要も出てきます。


関数判定の基本 typeof value === “function”

関数かどうかを判定する一番シンプルで正しい方法は、typeof を使うことです。

function isFunction(value) {
  return typeof value === "function";
}

console.log(isFunction(function () {}));  // true
console.log(isFunction(() => {}));        // true
console.log(isFunction(class A {}));      // true(ここは後で触れます)
console.log(isFunction({}));              // false
console.log(isFunction(123));             // false
console.log(isFunction(null));            // false
JavaScript

typeof value === "function" は、通常の関数宣言、関数式、アロー関数、クラス(class)などを「関数」として判定します。
実務で「呼び出せるかどうか」を知りたいだけなら、基本的にはこれで十分です。


なぜ「関数判定」が必要になるのか(実務目線)

オプションのコールバックを安全に呼びたい

よくあるパターンが、「オプションでコールバック関数を受け取る」ケースです。

function doSomething(data, onDone) {
  console.log("処理中:", data);

  if (typeof onDone === "function") {
    onDone(data);
  }
}

doSomething({ id: 1 }, (result) => {
  console.log("完了:", result);
});

doSomething({ id: 2 }); // コールバックなしでも安全に動く
JavaScript

ここで typeof onDone === "function" を挟んでおくことで、
「コールバックが渡されていない」「間違って文字列が渡された」といった場合でも、安全にスキップできます。

もし判定なしで onDone(data) と書いてしまうと、onDoneundefined のときに即エラーです。
「関数かどうかを確認してから呼ぶ」というのは、実務での基本マナーに近いです。

イベントハンドラやフックの登録

イベントやフックを受け付けるユーティリティでも、関数判定は必須です。

function registerHandler(handler) {
  if (!isFunction(handler)) {
    throw new Error("ハンドラは関数である必要があります");
  }

  // ここから下は handler が関数だと信じて書ける
  handlers.push(handler);
}
JavaScript

このように「入口で型をチェックしておく」と、
あとから「変な値が入っていて壊れた」ときに原因をすぐ特定できます。


クラスは「関数」として判定されることも知っておく

class で定義したものも、typeof で見ると "function" になります。

class User {}

console.log(typeof User);          // "function"
console.log(isFunction(User));     // true
JavaScript

これは仕様としてそういうものなので、「クラスも“呼び出し可能なもの”として扱う」なら問題ありません。
ただし、「実行時に fn() として呼びたいのか」「new Fn() としてコンストラクタとして使いたいのか」で意味が変わります。

多くの業務ユーティリティでは、「クラスが渡ってくるケースは少ない」「来ても関数として扱って問題ない」ことが多いので、
まずは「typeof value === 'function' で OK」と覚えておいて大丈夫です。

もし「クラスは除外したい」という特殊な要件が出てきたら、そのときに追加の判定を考えれば十分です。


関数判定ユーティリティを用意するメリット

isFunction に名前を与える意味

typeof value === "function" をそのまま書いてもいいのですが、
ユーティリティとして isFunction を用意しておくと、コードの意図が一気に読みやすくなります。

function isFunction(value) {
  return typeof value === "function";
}

if (isFunction(options.onClick)) {
  options.onClick();
}
JavaScript

typeof options.onClick === "function" よりも、
「ここでは“関数かどうか”を見ているんだな」という意図がはっきり伝わります。

また、プロジェクト全体で「関数判定は必ず isFunction を使う」と決めておけば、
あとから「クラスは除外したい」「Proxy の扱いを変えたい」などの要件が出ても、isFunction の中身だけ変えれば済みます。

「呼ぶ前に必ず isFunction」を習慣にする

実務での事故パターンの多くは、「関数だと信じて呼んだら関数じゃなかった」です。

callback(); // ここで落ちる
JavaScript

これを避けるために、「外から渡ってくる“かもしれない関数”は、呼ぶ前に必ず isFunction でチェックする」という習慣をつけると、
かなり多くのランタイムエラーを未然に防げます。


実務での具体的な利用イメージ

オプション付き処理フロー

例えば、「処理の前後に任意のフックを差し込める」ユーティリティを考えます。

function runWithHooks(task, { before, after } = {}) {
  if (isFunction(before)) {
    before();
  }

  const result = task();

  if (isFunction(after)) {
    after(result);
  }

  return result;
}

runWithHooks(
  () => {
    console.log("メイン処理");
    return 42;
  },
  {
    before: () => console.log("前処理"),
    after: (result) => console.log("後処理:", result),
  }
);
JavaScript

ここでは、beforeafter が渡されていない場合や、誤って文字列などが渡された場合でも、
isFunction のおかげで安全にスキップできます。

設定オブジェクト内のコールバック

設定オブジェクトに「任意のコールバック」を含めるパターンもよくあります。

const config = {
  onError: (err) => console.error("エラー:", err),
};

function doJob(config) {
  try {
    // 何か処理
    throw new Error("失敗した例");
  } catch (err) {
    if (isFunction(config.onError)) {
      config.onError(err);
    } else {
      console.error(err);
    }
  }
}

doJob(config);
doJob({}); // onError がなくても安全に動く
JavaScript

このように、「あってもなくてもいい関数」を扱うときに、関数判定ユーティリティは非常に相性がいいです。


小さな練習で感覚をつかむ

最後に、手を動かして慣れるためのミニ課題を提案します。

自分で isFunction を実装して、次の値を順番に渡してみてください。

  • 通常の関数 function () {}
  • アロー関数 () => {}
  • クラス class A {}
  • オブジェクト {}
  • 配列 []
  • 数値 123
  • 文字列 "fn"
  • null
  • undefined

コンソールに結果を出しながら、「どれが true で、どれが false になるか」を確認してみてください。
そのうえで、「外から渡ってくる“かもしれない関数”は、呼ぶ前に必ず isFunction でチェックする」という癖をつけられたら、
もう関数判定については実務レベルと言っていいです。

タイトルとURLをコピーしました