JavaScript においては typeof や constructor を参照するだけでは関数と class を見分けることができません。
// ただの関数 function myFunction() {} // class class MyClass {} // typeof では見分けられない typeof myFunction; // => 'function' typeof MyClass; // => 'function' // constructor でも見分けられない myFunction.constructor; // => Function MyClass.constructor; // => Function
関数と class を見分けるには toString() すると良いです。
myFunction.toString(); // => 'function myFunction() {}' MyClass.toString(); // => 'class MyClass {}'
class の場合、toString() した結果が 'class '
から始まります。
もし関数と class を判別する関数を書くとしたらこのようになります。
function isFunction(obj) { return typeof obj === 'function' && !obj.toString().match(/^class /); } function isClass(obj) { return typeof obj === 'function' && obj.toString().match(/^class /); }
isFunction の方の条件を obj.toString().match(/^function /)
にしていないのは、アロー関数を toString() した場合に結果が 'function '
から始まらないからです。
なぜこんなことになっている?
なぜ typeof や constructor だけでは関数と class が見分けられないのでしょうか。
それは class は本質的に関数と同じものだからです。
一昔前 [いつ?] まで JavaScript には class 構文が無くて、人々は function 構文を使って class 相当のものを書いていました。
例えばこのように、
class Rectangle { constructor(height, width) { this.height = height; this.width = width; } area() { return this.height * this.width; } } // ↓↓↓ かつてはこう書いていた function Rectangle(height, width) { this.height = height; this.width = width; } Rectangle.prototype.area = function() { return this.height * this.width; };
メソッドを定義するときに .prototype
を参照しています。このあたりの構文が JavaScript がプロトタイプベース言語であることを思い出させてくれますね。 *1
このように私たちが class 構文で定義しているものは関数でもあるので、二つを見分けるのには特殊なテクニックが必要になってしまっています。
区別する必要はない?
この件を ChatGPT に聞いてみると「JavaScript において class は関数と同等なので二つを区別する必要はありません」と言われました。本当でしょうか?
もう一度 function 構文を用いて定義した Rectangle クラスのコードを引用します。
// コンストラクタ function Rectangle(height, width) { this.height = height; this.width = width; } // area メソッドの定義 Rectangle.prototype.area = function() { return this.height * this.width; };
コンストラクタの中では return で値を返すことをしていません。そのため Rectangle(2, 3)
のように new を付けずに関数として Rectangle を呼び出すと返り値は undefined
になってしまいます。
というわけでやはり関数と class の区別は必要ですね。おのれ ChatGPT 。
まとめ
function 構文を用いた class 定義、久しぶりに書いたのでとても懐かしい気持ちになりました。
私からは以上です。
*1:実際には現代もこのあたりの事情はまったく変わっていなくて、 class 構文は単なる function 構文のシンタックスシュガーです