Update 2013-11-05:為了更好的解釋為什麼 'typeof null' 的結果是 'object',我查閱了實現的 C 原始碼。
在 JavaScript 中,typeof null
的結果是 object
,該結果錯誤地暗示了 null
是一個物件(null 並不是一個物件,而是一個基本資料型別(的值),詳情可以查閱我的博文 categorizing values)。這其實是一個 bug,但更糟的是這個 bug 是不能修復的,因為修復這個 bug 會使已經存在的程式崩潰。下面就讓我們探索一下這個 bug 的歷史。
typeof null
這個 bug 是 JavaScript 第一個版本遺留下來的。在這個版本中,所有值都儲存在 32 位的單元中,每個單元包含一個小的**型別標籤(1-3 bits)**以及當前要儲存值的真實資料。型別標籤儲存在每個單元的低位中,共有五種資料型別:
- 000: object - 當前儲存的資料指向一個物件。
- 1: int - 當前儲存的資料是一個 31 位的有符號整數。
- 010: double - 當前儲存的資料指向一個雙精度的浮點數。
- 100: string - 當前儲存的資料指向一個字串。
- 110: boolean - 當前儲存的資料是布林值。
如果最低位是 1
,則型別標籤標誌位的長度只有一位;如果最低位是 0
,則型別標籤標誌位的長度佔三位,為儲存其他四種資料型別提供了額外兩個 bit 的長度。
有兩種特殊資料型別:
- undefined(JSVAL_VOID) 的值是
-2**30(-2 的 30 次方)
(一個超出整數範圍的數字) - null(JSVAL_NULL) 的值是機器碼
NULL
指標(null 指標的值全是 0)。或者:object 型別的型別標籤 + 0 的引用。
現在可以很明顯地知道為什麼 typeof 操作符會認為 null 是物件了:typeof 操作符檢測 null 的型別標籤位時發現是 000
(存放機器碼 NULL 指標的儲存單元中的所有資料位都是 0,所以低三位也是 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)) { // (1) 檢查是否為 undefined
type = JSTYPE_VOID;
} else if (JSVAL_IS_OBJECT(v)) { // (2) 檢查是否為 object(低三位是 000)
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) // (3,4)
: ops->call != 0)) { // (3) 檢查是否為函式
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;
}
複製程式碼
上面程式碼的執行步驟:
- 在步驟(1)中,機器首先檢查值 v 是否是 undefined (VOID)。該檢查是通過比較值是否相等來完成的:
#define JSVAL_IS_VOID(v) ((v) == JSVAL_VOID) // 譯註:這是一個巨集定義
複製程式碼
- 步驟(2)檢查值是否具有 object 型別的型別標籤。如果該值具有 object 型別的型別標籤,並可以呼叫(3)或者其內部屬性 [[Class]] 標記它為函式(4),則該值是一個函式;否則,該值就是一個物件。這就是
typeof null
表示式生成的結果。 - 隨後檢查分別為是否為 number,string 以及 boolean。甚至沒有專門的步驟來檢查是否為 null,該檢查可通過如下的 C 巨集定義來實現:
#define JSVAL_IS_NULL(v) ((v) == JSVAL_NULL)
複製程式碼
這或許看起來是一個非常明顯的 bug,但是不要忘了實現 JavaScript 第一個版本的時間非常緊迫。
鳴謝:感謝 Tom Schuster(@evilpies) 指引我去看傳統 JavaScript 的原始碼。
Source Link ?
本文翻譯自 Dr. Axel Rauschmayer 的博文,侵刪。
轉載請聯絡作者並註明出處。
Translator Info ?
- GitHub
- Email: web.taox@gmail.com