【実務・中級編】 typeof演算子と関数オブジェクト – JavaScript実践ガイド

おい、調子はどうだい?
最近、後輩のコードレビューをしていて「おっ」と苦笑いさせられる瞬間があったんだ。曰く、「関数もオブジェクトのはずなのに、なんで `typeof` で調べると `’object’` じゃなくて `’function’` になるんですか? JavaScriptってやっぱりバグだらけの言語ですね」ってね。

……気持ちは痛いほど分かる。ECMAScriptの歴史を紐解けば、初期の設計ミスがそのまま後方互換性のために引き継がれている「JavaScriptの歴史的遺産(負債)」の一つだからだ。

しかし、現場でバリバリ動くプロのフロントエンドエンジニアを目指すなら、「仕様だから」で片付けるんじゃなくて、「ブラウザの裏側で何が起きていて、なぜその判定が必要なのか」を骨の髄まで理解しておく必要がある。今回は、この `typeof` と関数オブジェクトの奇妙な関係について、実務の視点から徹底的に解き明かしてやろう。

—

1. 現場を混乱させる「typeof」の二面性

まずは基本の復習からいこう。JavaScriptのデータ型は、大きく分けると「プリミティブ型」と「オブジェクト型」の2つに分類される。そして、ある値の型をサクッと調べたいときによく使うのが `typeof` 演算子だ。

直感的に考えれば、関数(Function)もJavaScriptの世界では立派なオブジェクトの一種なのだから、`typeof` を使えば `’object’` が返ってくるのが筋道というものだ。しかし、現実はこうだ。

// 普通のオブジェクト
const user = { name: ‘Taro’ };
console.log(typeof user); // 期待通り ‘object’ が返る

// 関数
function greet() {
console.log(‘Hello!’);
}
console.log(typeof greet); // あれ? ‘function’ が返ってくる!

「あれ?」と思ったそこの君、鋭い。
オブジェクトの一種であるはずの関数が、なぜか `’object’` ではなく独立した `’function’` という文字列で評価される。この仕様のせいで、型ガード(Type Guard)を書くときに「関数なのか、それとも普通のプレーンオブジェクトなのか」で迷子になる中級エンジニアが後を絶たない。

—

2. 歴史的背景:なぜ関数だけ特別扱いされるのか?

この謎を解く鍵は、1995年のJavaScript誕生初期、Brendan Eich氏がわずか10日間でこの言語を書き上げた時代にさかのぼる。

当時、JavaScriptの初期実装では、値の型情報を表現するためにメモリ上の「タグ(目印)」を使っていた。オブジェクトを表すタグの値は `0`。そして、「呼び出し可能なオブジェクト(Callable Object=つまり関数)」を区別するために、別の内部的な判定が必要だった。

結果として、`typeof` 演算子の実装において、内部的に「Callable(呼び出し可能)であるか?」をチェックし、そうであれば `’function’` という文字列を返す仕様が組み込まれたのだ。

もし今さらこの挙動を変えて、関数に対して `typeof` が `’object’` を返すように仕様変更したらどうなるか? 世界中の数百万というレガシーなWebアプリケーションやライブラリが盛大にクラッシュするだろう。
そう、Webの最大の武器である「圧倒的な後方互換性(Backward Compatibility)」を維持するために、この仕様は今も生き続けている。つまり、これはバグではなく「仕様という名の歴史の呪い」なのだ。

—

3. 「関数はオブジェクトである」という揺るぎない事実

ここで勘違いしてほしくないのは、`typeof` が `’function’` を返すからといって、関数がオブジェクトではないわけではないという点だ。

JavaScriptにおいて、関数は「プロパティを持てる、メソッドを生やせる、プロトタイプチェーンを持つ」紛れもない第一級オブジェクト(First-class Object)だ。証拠に、関数に対しても普通にプロパティを追加できる。

function sayHello() {
console.log(‘こんにちは!’);
}

// 関数に関数自身のプロパティを生やす(これも立派なオブジェクトの証拠)
sayHello.counter = 0;
sayHello.increment = function() {
this.counter++;
console.log(`呼び出し回数: ${this.counter}`);
};

sayHello.increment(); // 呼び出し回数: 1
sayHello.increment(); // 呼び出し回数: 2

// インスタンスのprototypeも持っている
console.log(typeof sayHello.prototype); // ‘object’

つまり、実務的なメンタルモデルとしては、「関数は、内部に『[[Call]]』という隠しスロット(実行可能な機能)を持った、特殊なオブジェクトである」と捉えるのが最も正確だ。

—

4. 実務で役立つ!堅牢な型判定のベストプラクティス

さて、ここからが現場で一番重要な話だ。
「関数なのか、オブジェクトなのか、あるいは配列なのか」を実務のコードで安全に判定するにはどうすればいいのか? `typeof` だけに頼っていると、思わぬバグを踏む。

以下のサンプルコードを見てほしい。実務のユーティリティ関数としてもそのままコピペして使えるように書いておいた。

/

  • 堅牢な型判定を行うためのユーティリティ集
  • 現場のコードレビューでドヤ顔できるレベルの安全性を担保する

/

// 1. 関数の判定
// typeof で ‘function’ を見るのが一番手っ取り早く、かつ安全
function isFunction(value) {
return typeof value === ‘function’;
}

// 2. プレーンオブジェクトの判定(配列やnull、関数を除外する)
// 開発現場で一番よくやるミスが、typeof null が ‘object’ になることや、
// 配列を typeof で判定してしまってハマるパターン。
function isPlainObject(value) {
// null と非オブジェクトを弾く
if (value === null || typeof value !== ‘object’) {
return false;
}

// 配列や関数を弾くために原型(Prototype)をチェック
const proto = Object.getPrototypeOf(value);
return proto === Object.prototype || proto === null;
}

// — 実戦テスト —
const myFunc = () => {};
const myArr = [1, 2, 3];
const myObj = { a: 1 };
const myNull = null;

console.log(‘— 関数判定 —‘);
console.log(isFunction(myFunc)); // true
console.log(isFunction(myObj)); // false

console.log(‘— プレーンオブジェクト判定 —‘);
console.log(isPlainObject(myObj)); // true
console.log(isPlainObject(myArr)); // false (配列はプレーンオブジェクトではない)
console.log(isPlainObject(myFunc)); // false (関数もプレーンオブジェクトではない)
console.log(isPlainObject(myNull)); // false (nullの罠を回避)

プロからのワンポイントアドバイス

特にライブラリの開発や、複雑なステート管理を持つフロントエンドアーキテクチャ(Reactのカスタムフックや状態管理の内部処理など)では、`typeof null === ‘object’` や、今回テーマにした `typeof function === ‘function’` のような「言語仕様の癖」を完全に理解した上で型ガードを組む必要がある。

TypeScriptを使っている場合はコンパイラが型を絞り込んでくれるが、実行時(Runtime)のバリデーションやAPIレスポンスのパース処理などでは、こうしたJavaScript本来の挙動を知っているかどうかが、バグの少ない強靭なコードを書ける分かれ道になるんだ。

—

まとめ

  • `typeof fn === ‘function’` となるのは、歴史的経緯と後方維持のため。
  • 関数も本質的には「実行可能スロットを持ったオブジェクト」であることに変わりはない。
  • 実務では `typeof` のクセ(`null` や `array` の扱い)を理解し、必要に応じてプロトタイプチェーン等も併用した堅牢な判定ロジックを書こう。

仕様の裏側にあるストーリーを知ると、日々のコーディングが少しだけ楽しく、そして深くなるはずだ。さあ、今日のコードにもこの知見を早速活かしてくれ!

コメント

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