typeof nullの結果が「object」である理由を本当に知っていますか?
これまでのところ、ECMAScript標準で定義されているデータ型は、Undefined、Null、Number、Boolean、String、Symbol、BigInt、およびObjectの8つです。
変数のデータ型を判別するために、JavaScriptはtypeof演算子も提供します。
データ型の値は、typeof演算子の操作後の出力値に対応します。
データの種類 | 値 | 結果 |
---|---|---|
未定義 | 未定義 | '未定義' |
ヌル | ヌル | 「オブジェクト」 |
数 | 1、1.0、NaN、インフィニティ | '数' |
ブール値 | 真/偽 | 「ブール値」 |
ストリング | ''、 'abc' | 'ストリング' |
シンボル | Symbol()、Symbol( '123') | 'シンボル' |
BigInt | 0n、1n | 'bigint' |
オブジェクト | {}、[] | 「オブジェクト」 |
オブジェクト | 関数(){} | '関数' |
観察を通して、問題を見つけることができます-typeof演算子は、プリミティブ型の値nullをオブジェクトとして誤って判断します
typeof null === 'object'//true
これにより、typeof x === 'object'が発生し、xもnullになる可能性があります
この問題は、JavaScript 1の最初のバージョンにまでさかのぼることができます。このバージョンでは、単一の値がスタック内の32ビットストレージユニットを占有し、32ビットストレージユニットはタイプタグ(1〜3ビット)に分割できます。)そして実際のデータ、タイプラベルは下位ビットに格納され、5つのタイプに分けることができます。
(1)
図2に示すように、0、1、2番目のビットがすべて0の場合、typeofはタイプを「オブジェクト」と判断します。
(2)
図2に示すように、0番目のビットが1の場合、typeofはタイプを「数値(整数)」と判断します。
(3)
図2に示すように、0番目と2番目のビットが両方とも0で、1番目のビットが1の場合、判定タイプのタイプは「数値(浮動小数点)」です。
(4)
図2に示すように、0番目のビットと1番目のビットが両方とも0で、2番目のビットが1の場合、typeofはタイプを「文字列」と判断します。
(5)
図2に示すように、1番目と2番目のビットが両方とも1で、0番目のビットが0の場合、typeofは型を「ブール値」と判断します。
さらに、2つの特殊なケースがあります。
未定義:整数-2 ^ 30(整数の範囲外の数値)
null:0から31までの数字はすべて0(0、1、2番目のビットがすべて0の場合、typeofが型を「オブジェクト」と判断するという条件を満たすだけです。)
次のコードは、この問題をよりよく示しています(ソース)。
JS_PUBLIC_API(JSType)
JS_TypeOfValue(JSContext *cx, jsval v)
{
JSType type = JSTYPE_VOID;
JSObject *obj;
JSObjectOps *ops;
JSClass *clasp;
CHECK_REQUEST(cx);
if (JSVAL_IS_VOID(v)) {
type = JSTYPE_VOID;
} else if (JSVAL_IS_OBJECT(v)) {
obj = JSVAL_TO_OBJECT(v);
if (obj &&
(ops = obj->map->ops,
ops == &js_ObjectOps
? (clasp = OBJ_GET_CLASS(cx, obj),
clasp->call || clasp == &js_FunctionClass)
: ops->call != 0)) {
type = JSTYPE_FUNCTION;
} else {
type = JSTYPE_OBJECT;
}
} else if (JSVAL_IS_NUMBER(v)) {
type = JSTYPE_NUMBER;
} else if (JSVAL_IS_STRING(v)) {
type = JSTYPE_STRING;
} else if (JSVAL_IS_BOOLEAN(v)) {
type = JSTYPE_BOOLEAN;
}
return type;
}
このため、typeof null === 'object'はtrueです。