到目前為止,ECMAScript 標準中定義了8種資料型別,它們分別是Undefined、Null、Number、Boolean、String、Symbol、BigInt、Object。
為了判斷變數的資料型別,JavaScript還提供了typeof操作符。
資料型別中的值通過typeof操作符操作過後輸出的值對應表格:
資料型別 | 值 | 結果 |
---|---|---|
Undefined | undefined | 'undefined' |
Null | null | 'object' |
Number | 1、1.0、NaN、Infinity | 'number' |
Boolean | true、false | 'boolean' |
String | '' 、'abc' | 'string' |
Symbol | Symbol()、Symbol('123') | 'symbol' |
BigInt | 0n、1n | 'bigint' |
Object | {}、[] | 'object' |
Object | function(){} | 'function' |
通過觀察,我們可以發現一個問題——typeof操作符錯誤的將一個原始型別值null判斷為object
typeof null === 'object'//true
這將導致typeof x === 'object'時,x還有可能是null
這個問題的產生可以追溯到JavaScript的第一個版本[1],在這個版本中,單個值在棧中佔用32位的儲存單元,而這32位的儲存單元又可以劃分為型別標籤(1-3位)和實際資料,型別標籤儲存於低位中,具體可以分成5種:
(1)
如圖[2],當第0位、第1位和第2位皆為0時,typeof判斷型別為'object';
(2)
如圖[2:1],當第0位為1時,typeof判斷型別為'number(整數)';
(3)
如圖[2:2],當第0位與第2位皆為0,而第1位為1時,typeof判斷型別為'number(浮點數)';
(4)
如圖[2:3],當第0位與第1位皆為0,而第2位為1時,typeof判斷型別為'string';
(5)
如圖[2:4],當第1位與第2位皆為1,而第0位為0時,typeof判斷型別為'boolean';
此外還有兩種特殊情況:
undefined:整數−2^30 (整數範圍之外的數字)
null:第0位到第31位皆為0(正好滿足當第0位、第1位和第2位皆為0時,typeof判斷型別為'object'的條件)
下面的程式碼更好的說明了這個問題(來源):
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。