typeof null 为什么返回 object?(“typeof null”的历史)

读到一篇介绍typeof null的文章,感觉还不错,特此记录

原文

Update 2013-11-05: I take a look at the C code of typeof to better explain why typeof null results in 'object'.

In JavaScript, typeof null is 'object', which incorrectly suggests that null is an object (it isn’t, it’s a primitive value, consult my blog post on categorizing values for details). This is a bug and one that unfortunately can’t be fixed, because it would break existing code. Let’s explore the history of this bug.
The “typeof null” bug is a remnant from the first version of JavaScript. In this version, values were stored in 32 bit units, which consisted of a small type tag (1–3 bits) and the actual data of the value. The type tags were stored in the lower bits of the units. There were five of them:

000: object. The data is a reference to an object.
1: int. The data is a 31 bit signed integer.
010: double. The data is a reference to a double floating point number.
100: string. The data is a reference to a string.
110: boolean. The data is a boolean.
That is, the lowest bit was either one, then the type tag was only one bit long. Or it was zero, then the type tag was three bits in length, providing two additional bits, for four types.
Two values were special:

undefined (JSVAL_VOID) was the integer −230 (a number outside the integer range).
null (JSVAL_NULL) was the machine code NULL pointer. Or: an object type tag plus a reference that is zero.
It should now be obvious why typeof thought that null was an object: it examined its type tag and the type tag said “object”. The following is the engine’s code for 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;
    }
The steps performed by the above code are:
At (1), the engine first checks whether the value v is undefined (VOID). This check is performed by comparing the value via equals:
    #define JSVAL_IS_VOID(v)  ((v) == JSVAL_VOID)
The next check (2) is whether the value has an object tag. If it additionally is either callable (3) or its internal property [[Class]] marks it as a function (4) then v is a function. Otherwise, it is an object. This is the result that is produced by typeof null.
The subsequent checks are for number, string and boolean. There is not even an explicit check for null, which could be performed by the following C macro.
    #define JSVAL_IS_NULL(v)  ((v) == JSVAL_NULL)
This may seem like a very obvious bug, but don’t forget that there was very little time to finish the first version of JavaScript.
Acknowledgement: Thanks to Tom Schuster (@evilpies) for pointing me to the classic JavaScript source code.

译文

2013-11-05更新,为了解释typeof null的结果为什么是"object",我去看了typeof的C语言实现。

在JavaScript中,typeof null类型是‘object’,这错误地暗示了null是一个对象(它不是,它是一个原始值)。这是一个错误,不幸的是,这个错误无法修复,因为它会破坏现有的代码。让我们来研究一下这个bug的历史。

"typeof null"的bug来源于JavaScript的第一个版本的错误实现。在这个版本中,用32位存储一个值,包括一个表示类型的标记(1-3位)和值的实际数据。类型标记存储在低位上,一共有5种:

  • 000: object. 这是一个对象
  • 1: int. 31位有符号整数
  • 010: double. 双浮点数
  • 100: string. 字符串
  • 110: boolean. 布尔值
    也就是说,最低位如果是1,那么类型标记只有1位长;如果是0,那么类型标记有3位长,为4种类型提供两个额外的位。
    有两个值是特殊的:
  • undefined (JSVAL_VOID)是整数−230(整数范围之外的数字)。
  • null (JSVAL_NULL)是机器码空指针。或者:一个对象类型标签加上一个零的引用。
    现在我们很清楚为什么typeof为什么会认为null是一个对象了,它检查了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)处,引擎首先检查值v是否未定义(VOID)。通过检查比较的值是否相等:
#define JSVAL_IS_VOID(v)  ((v) == JSVAL_VOID)
  • 下一个检查(2)是该值是否有对象标记。如果它是可调用的(3)或者它的内部属性[[Class]]将它标记为函数(4),那么v就是一个函数。否则,它就是一个对象。这是typeof null生成的结果。
  • 接下来的检查是数字、字符串和布尔值。甚至没有显式地检查null,这可以通过下面的C宏来执行。
 #define JSVAL_IS_NULL(v)  ((v) == JSVAL_NULL)

这似乎是一个非常明显的错误,但是不要忘记,用来完成第一个JavaScript版本的时间非常少。
感谢Tom Schuster (@evilpies)为我指出了经典的JavaScript源代码。

原文链接

The history of “typeof null”

MDN typeof

typeof

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值