为什么 "typeof null" 的结果为 "object" ?
在 JavaScript 中typeof null
的结果为 "object"
,这是从 JavaScript 的第一版遗留至今的一个 bug。并且,因为影响太广已经永远无法修复这个 bug 了。我们来挖一挖坟,看看这个 bug 是怎么来的。
在第一版的 JavaScript 中,变量的值被设计保存在一个 32 位的内存单元中。该单元包含一个 1 或 3 位的类型标志,和实际数据的值。类型标志存储在单元的最后。包括以下五种情况:
- 000:object,数据为对象的引用
- 1:int,数据为 31 位的有符号整型
- 010:double,数据为一个双精度浮点数的引用
- 100:string,数据为一个字符串的引用
- 110:boolean,数据为布尔类型的值
除此之外,还有两种特殊情况:
- undefined 负的 2 的 30 次方(超出当时整型取值范围的一个数)
- null 空指针
显而易见,null 的存储单元最后三位和 object 一样是 000
。然后让我们再看一下 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)判断值
v
是否为undefined(VOID)
,这个检查通过下面的比较来判断:#define JSVAL_IS_VOID(v) ((v) == JSVAL_VOID)
- 接下来,在步骤(2)判断值是否包含
object
标志,并且通过(3)判断值是否可以调用,或者(4)判断值是否在属性[[Class]]
中被标记为函数,来判断值为函数,否则为对象。 - 接下来的步骤,依次判断是否为
number
、string
和boolean
。
在步骤(2)中 null
会被判断为 object
,其实避免这个 bug 的方式很简单,在步骤(2)之前显示的检查值是否为 null
:
#define JSVAL_IS_NULL(v) ((v) == JSVAL_NULL)
考虑 JavaScript 之父在非常紧迫的时间里完成了这门语言的设计与开发,我们就原谅他吧 :pensive:
本作品采用《CC 协议》,转载必须注明作者和本文链接
推荐文章: