関数判定とは何を見分けたいのか
「関数判定」は、その値が「本当に関数かどうか」を見分けることです。
業務コードでは、「コールバックを受け取る」「オプションでフック関数を渡せる」「イベントハンドラを登録する」など、
「渡された値が関数なら呼ぶ、関数でなければスルーする」という場面がとても多くあります。
ここで関数かどうかをきちんと判定できないと、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
JavaScripttypeof 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) と書いてしまうと、onDone が undefined のときに即エラーです。
「関数かどうかを確認してから呼ぶ」というのは、実務での基本マナーに近いです。
イベントハンドラやフックの登録
イベントやフックを受け付けるユーティリティでも、関数判定は必須です。
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();
}
JavaScripttypeof 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ここでは、before と after が渡されていない場合や、誤って文字列などが渡された場合でも、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" nullundefined
コンソールに結果を出しながら、「どれが true で、どれが false になるか」を確認してみてください。
そのうえで、「外から渡ってくる“かもしれない関数”は、呼ぶ前に必ず isFunction でチェックする」という癖をつけられたら、
もう関数判定については実務レベルと言っていいです。
