仕様がそう決めているからです。
typeof の結果は仕様の表で決まっており、Null には "object" と書かれています。
理由は仕様に書かれていません。
function isObject(value) {
return typeof value === 'object';
}
console.log(isObject({}));
console.log(isObject(null));古い実装の値の持ち方に由来する、という経緯が広く知られています。ただし仕様はその理由を述べていないので、ここでは「そう決まっている」以上のことは書きません。直そうという提案は、既存のコードを壊すため採用されていません。
何が困るのか
typeof でオブジェクトを判定すると、null が紛れ込みます。
このあと value.name を読めば落ちます。
→ Cannot read properties of undefined
null を外せば直ります。
配列も true になります。 typeof は配列を区別しません。
配列かどうかは Array.isArray() で見てください。
typeof が "object" を返すもの
typeof は5つとも同じ答えを返します。
Object.prototype.toString.call() なら区別できます。
| 調べたいこと | 使うもの |
|---|---|
null か |
value === null |
null か undefined か |
value == null |
| 配列か | Array.isArray(value) |
| 素のオブジェクトか | typeof v === 'object' && v !== null && !Array.isArray(v) |
| 細かい種類 | Object.prototype.toString.call(value) |
null の見分け方
=== null…nullだけ== null…nullとundefinedの両方
== を使ってよいのはこの場面だけです。
→ なぜ == ではなく === を使うのか
null と undefined は役割が違う
undefined… まだ何も入っていない。処理系が付けるnull… 意図的に「無い」と入れた。人が付ける
だから既定値は undefined のときだけ働きます。
null は「値として入っている」ので、置き換えられません。
計算に使うと、さらに違いが出ます。
null は0として扱われ、undefined は NaN になります。
どちらも「無い」つもりで足すと、結果が変わります。
typeof が返すのは8種類だけ
{} も [] も new Date() も、全部 "object" に潰されます。
クラスや組み込みの Array は "function" です。
素のオブジェクトかを見る
Date が true になっています。 この書き方は「配列でないオブジェクト」までしか見ません。
そこまで厳密に見たい場面は多くありません。
必要なら Object.prototype.toString.call(value) === '[object Object]' を使ってください。
等価と比較で規則が違う
null == 0 は偽なのに、null >= 0 は真です。
== は null を特別扱いして「undefined としか等しくない」と決めていますが、
>= は数値に変換してから比べます。null は0になります。
「等しくないのに、以上ではある」という結果になります。
null を数の比較に混ぜないでください。
よくある誤解
「typeof があれば型は分かる」
分かりません。typeof が返すのは8種類だけで、
オブジェクトの中身は全部 "object" です。
「null は typeof で判定できないから使えない子」
そうではありません。=== null で正確に判定できます。
判定できないのは typeof だけです。
「null を返しておけば安全」
呼び出し側が null を想定していなければ、そこで落ちます。
「無い」を返すなら、無いことが起きうると呼び出し側に伝わる形
(型・命名・書類)にしてください。
自分で確かめる
上のコードは書き換えて実行できます。
typeof function(){}を試す("object"ではない)Object.prototype.toString.call(null)とString(null)を並べるnull == 0とnull >= 0を並べる(等価と比較で規則が違う)
予想してから実行してください。
まとめ
- 仕様がそう決めている。 理由は仕様に書かれていない
typeof value === 'object'はnullも通す。&& value !== nullを足す- 配列も
"object"。配列かどうかはArray.isArray() nullだけなら=== null、undefinedも含めるなら== nullundefinedは処理系が付け、nullは人が付ける- 既定値は
undefinedのときだけ働く。nullでは働かない