【譯】談談“typeof null為object”這一bug的由來
很多前端初級開發者也許並不深究typeof null為何為Object?想更深地瞭解這一bug的由來,可以參閱Dr. Axel Rauschmayer關於"typeof null"的歷史這篇文章。
原文連結:www.2ality.com/2013/10/typ…
我看了下C語言關於typeof
的規範,它對於typeof null
為何結果是’object
’有更好的解釋。
在JavaScript中,typeof null
的結果是'Object
',它錯誤地暗示null
是一個物件,實際上它是一個原始值。我在上一篇文章也提到了這是JS的一大bug,不幸的是這並不能解決,因為這將破壞現有規範,接下來解釋下這個bug的歷史。
“typeof null
”的錯誤從JavaScripts第一個版本開始就已經存在了。在這個版本,值以32位為單位儲存,由小型標籤(1-3位)和值的實際資料組成。型別標籤儲存在單元的低位中。 其中有五種:
object int double string boolean
也就是說,最低位是1,然後型別標籤只有1位長,即int
型。 或者最低位為0,那麼型別標籤的長度為3位,為其餘4種類型提供了兩個附加位。
但有2個值是特殊的:
undefined 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
(VOID),它通過==
做了這樣的比較:
#define JSVAL_IS_VOID(v)((v) == JSVAL_VOID) 複製程式碼
-
下一個(2)是檢測該值是否具有
object type
。如果它可使用call
被呼叫(3)或其存在內部屬性[[Class]]
標記為函式(4),則v是函式。 否則,它是一個物件。 這是由typeof null
生成的結果。 -
後續檢查是針對
number
,string
和boolean
,甚至沒有明確檢查null
。這可以由以下C語言巨集執行。
#define JSVAL_IS_NULL(v)((v) == JSVAL_NULL)` 複製程式碼
這看似是一個非常明顯的bug,但不要忘記,第一個版本的JavaScript完成只用了極少的時間,具體可以看看JavaScript的誕生。
如果覺得文章對你有些許幫助,歡迎在我的GitHub部落格 點贊和關注,感激不盡!