更新時間2013-11-05:為了更好的解釋為什么typeof null的結果是object,我看了一下C代碼的實現(譯者注:Javascript源碼)。
在Javascript語言中,typeof null的結果是object。這樣就會錯誤的認為null是一個對象(事實上,它不是一個對象,它是原始值,詳細的說明可以看我的發布的博客
Javascript中的數據類型 )。很不幸,它是一個不能修復的bug,因為修復后會破壞掉現有的代碼。那么。讓我們探索一下這個bug的起源吧。
這個bug是第一版Javascript留下來的。在這個版本,數值是以32字節存儲的,由標志位(1~3個字節)和數值組成。標志位存儲的是低位的數據。這里有五種標志位:
- 000:對象,數據是對象的應用。
- 1:整型,數據是31位帶符號整數。
- 010:雙精度類型,數據是雙精度數字。
- 100:字符串,數據是字符串。
- 110:布爾類型,數據是布爾值。
最低位有一位,那么標志位只有一個1字節長度;或者是零位,標志位有3個字節長度,多出兩個了字節,一共多出四種類型。
有兩個特殊的數值:
- undefined(JSVAL_VOID)是-2^30(一個超出整數范圍的數字)
- null(JSVAL_NULL)是機器代碼的空指針,一個對象類型的引用,值是零。
這樣就很明顯的知道為什么typeof null的值是object了:它檢查了標志位的類型,標志位表明它是個對象。下面是源碼關於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) type = JSTYPE_VOID; } else if (JSVAL_IS_OBJECT(v)) { // (2) 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),代碼首先會檢查數值是不是undefined(VOD)。是通過比較值:
#define JSVAL_IS_VOID(v) ((v) == JSVAL_VOID) //譯者注:比較值是否與undefined值相等
- 注釋(2),判斷值是否有對象的標志位。如果它是可調用的,或者說通過內部屬性[[Class]]可以表明它是一個函數。否者它就是一個對象。這也就是typeof null的執行結果。
- 隨后是針對數字、字符串和布爾值的檢測。這里並沒有明確的檢測null,源碼中本應該有這樣的實現:
#define JSVAL_IS_NULL(v) ((v) == JSVAL_NULL) //譯者注:本身應該存在一個類似檢測undefined一樣的方法,來檢測null,但是事實證明它並沒有
這看起來像是一個很明顯的bug,但是不要忘了Javascript的第一個版本花費了很少的時間完成的。
感謝:感謝 Tom Schuster (@evilpies)告訴我Javascript源碼
源碼地址