为什么 "typeof null" 的结果为 "object" ?

翻译 & 改编自 http://2ality.com/2013/10/typeof-null.html

在 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]] 中被标记为函数,来判断值为函数,否则为对象。
  • 接下来的步骤,依次判断是否为 numberstringboolean

在步骤(2)中 null 会被判断为 object,其实避免这个 bug 的方式很简单,在步骤(2)之前显示的检查值是否为 null

#define JSVAL_IS_NULL(v)  ((v) == JSVAL_NULL)

考虑 JavaScript 之父在非常紧迫的时间里完成了这门语言的设计与开发,我们就原谅他吧 :pensive:

本作品采用《CC 协议》,转载必须注明作者和本文链接
Night gathers, and now my watch begins.
讨论数量: 1

我建议你修改你的标题,这样的标题并不好,带有攻击性和误导

4年前 评论
开根方的茄子 (楼主) 4年前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!