为什么typeof null是"object"?

370

我正在阅读《Web开发专业JavaScript》第四章,书中告诉我原始类型有五种: undefined, null, boolean, number和string。

既然null是一种原始类型,那么为什么typeof(null)会返回"object"

这是否意味着null被按引用传递(我在这里假设所有对象都是按引用传递),从而使其不是一种原始类型?


101
回答:因为规格书上是这么写的。这通常被视为错误。 - SLaks
7
注意,typeof是一个操作符而不是函数(实际上你可以省略它后面的括号),因此在这里谈论传递引用是没有意义的。书籍《JavaScript: The Good Parts》在附录A的A.6节“糟糕的部分”中实际上提到了typeof null === 'object'的事实。 - John Sonderson
2
那么,除了使用 typeof 来检查变量所持有的值的类型之外,我们应该使用什么呢?我很想知道它是在 (boolean、string、number、array、object、function、symbol、null、undefined、NaN) 中的哪一个。 - Costa Michailidis
3
顺便说一下,typeof 是一个操作符而不是一个函数,因此你的代码中的括号是不必要的 - 你可以直接使用语法 typeof null - Oliver Sieweke
2
我建议在您的代码库中进行查找和替换,从 (typeof +(\w+) +={2,3} +"object")($2 && $1),以便在可能存在的任何地方修复此问题。这将把 typeof arg === "object" 转换为 (arg && typeof arg === "object") - Seph Reed
显示剩余3条评论
11个回答

301

MDN关于typeof运算符行为的页面

null

// 这个问题早在JavaScript出现之初就存在了
typeof null === 'object';

在JavaScript最初的实现中,JavaScript值被表示为类型标签和值。对象的类型标签是0。null被表示为NULL指针(在大多数平台上为0x00)。因此,null具有0作为类型标记,导致typeof返回值为“object”。(参考文献)

为ECMAScript提出了一个修复方案(通过选择加入),但是被拒绝了。这将导致typeof null === 'null'


205
很遗憾,这个改变没有被应用到严格模式中…… - Ingo Bürk
1
人们一直在利用这个怪癖,如果这个被拒绝了,很多代码都需要改变。我猜测如此。 - Alex Pappas
人们为什么会将其用作空值检查,这根本没有意义。它不具备直观的意义,所以他们为什么要使用它呢?现在由于糟糕的编码,无法添加更改。 - Emobe
2
向后不兼容的变化,例如使 typeof null === 'null' 总是会引起比任何人都能预测到的更为深远和广泛的问题。我也讨厌 typeof null === 'object',但仅基于理想而争辩它不应该这样是无知的争论。 - John Miller

83
如果null是一个原始值,为什么typeof(null)返回"object"呢? 因为规范是这样规定的

11.4.3 typeof运算符

产生式UnaryExpression : typeof UnaryExpression的计算结果如下:

  1. val为计算UnaryExpression的结果。
  2. 如果Type(val)是Reference,则
       a. 如果IsUnresolvableReference(val)为true,则返回"undefined"。
       b. 令valGetValue(val)的结果。
  3. 根据表20,返回一个由Type(val)决定的字符串。

enter image description here


2
据我所知,除了 nullundefined,你可以在任何东西上调用方法。 - Matt Ball
5
@peter,你不能在一个字符串原始类型上调用方法,但值得庆幸的是,当你使用其中一个原始类型与属性引用操作符( .[ ])结合使用时,字符串原始类型(以及数字和布尔原始类型)会被隐式自动地“自动装箱”为String、Number和Boolean包装器。 - Pointy

35

正如已经指出的那样,规范是这么说的。但由于JavaScript的实现先于ECMAScript规范的编写,并且规范小心翼翼地不纠正初始实现的怪异行为,因此仍然存在一个合理的问题,即首先为什么要这样做。Douglas Crockford 称其为错误。Kiro Risk 认为这有点道理

其背后的原因是,与undefined不同,null经常被用在对象出现的地方。换句话说,null通常用来表示对对象的空引用。当Brendan Eich创建JavaScript时,他遵循了相同的范例,返回"object"也讲得通(可以说)。事实上,ECMAScript规范将null定义为表示任何对象值的有意缺席的原始值(ECMA-262,11.4.11)。


5
由于我无法找到现在的视频,所以我会发布这个内容仅供好奇的人参考,不含任何参考资料:Crockford解释了如何在解析null类型时将零值指向类型数组中的零索引元素,因此这是一个明显的开发错误,微软工程师在反编译和重新编译JS时意外地传播了这个错误。 - Áxel Costas Pena

10

来自书籍 YDKJS

这是JS中长期存在的一个bug,但很可能永远不会被修复。因为太多Web上的代码依赖这个bug,修复它会导致更多的bug!


3
不要相信书上的一切内容。我真的很喜欢那本书,但我不能认为那是一个错误,因为ECMA对JavaScript规范声明null的类型必须是一个对象。 - andreasonny83
2
@andreasonny83 这篇博客文章 解释了为什么这是一个 bug。规范声明 typeof nullobject 只是意味着规范选择将该 bug 作为规范的一部分。无论如何,这就是为什么我认为它是一个 bug 的原因。 :) - BobRodes
1
Brendan Eich解释为什么这是有意的。 typeof null ===“ object”不是错误,而是为了适应Java互操作性而产生的二阶效应。 - Ben Aston

9

null

// 这是 JavaScript 从一开始就存在的
typeof null === 'object';

简短回答:

这是自 ECMAScript 发布以来的一个错误,但不幸的是无法修复,因为这会破坏现有的代码。

解释:

然而,在 JavaScript 中将 null 定义为对象实际上是有一个逻辑解释的。

在 JavaScript 的初始版本中,值存储在32位单元中,其中包括一个小的类型标记(1-3位)的实际数据。类型标记存储在单元的低位中。共有五种类型标记:

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.

对于所有对象,类型标记位都是000。从JavaScript的第一个版本开始,null被认为是一个特殊值。null是空指针的表示形式。然而,在JavaScript中没有像C语言那样的指针。因此,null仅仅意味着没有任何值或空,并且用所有的0来表示。因此,它的32位都是0。所以每当JavaScript解释器读取null时,它将前3位视为类型“object”。这就是为什么typeof null返回“object”的原因。

enter image description here

资源: https://2ality.com/2013/10/typeof-null.html#:~:text=In%20JavaScript%2C%20typeof%20null%20is,it%20would%20break%20existing%20code
注意:
ECMAScript提出了一项修复(通过选择加入),但被拒绝了。 这将导致

typeof null === 'null'。


6

1
你的参考资料中没有说明这是一个错误。 - user2867288
1
规范中并没有说明 typeof 应该返回除了 "undefined"、"object"、"boolean"、"number"、"string"、"function" 和 "symbol"(ECMAScript 2015)之外的任何内容。 - Brad Kent

1

++作者的回答是:

我认为修复 typeof 已经太晚了。对于 typeof null 提出的更改将会破坏现有代码。

时间已经晚了。自从微软创建了自己的 JavaScript 引擎并复制了第一个引擎版本的所有功能和缺陷以来,所有后续的引擎都复制了这个错误,现在修复它已经太晚了。

JS_TypeOfValue(JSContext *cx, jsval v)
{
    JSType type = JSTYPE_VOID;
    JSObject *obj;
    JSObjectOps *ops;
    JSClass *clasp;

    CHECK_REQUEST(cx);
    if (JSVAL_IS_VOID(v)) {
        type = JSTYPE_VOID;
    } else if (JSVAL_IS_OBJECT(v)) {
        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)
                : ops->call != 0)) {
            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;
}

那个代码从哪里来的? - user3840170

1
ECMAScript规范确定了以下语言数据类型

6.1.1 未定义类型
6.1.2 空类型
6.1.3 布尔类型
6.1.4 字符串类型
6.1.5 符号类型
6.1.6 数值类型
    6.1.6.1 数字类型
    6.1.6.2 大整数类型
6.1.7 对象类型

由于历史原因,typeof运算符在两种情况下与此分类不一致:
  • typeof null == "object":这是不幸的,但我们必须接受。
  • 函数对象的typeof计算结果为"function",尽管根据规范它的数据类型为对象
另一个操作符 -- instanceof -- 可以用来判断一个对象是否从某个原型继承而来。例如,[1,2] instanceof Array 将会返回 true。
判断一个值是否为对象的一种方法是使用 Object 函数:
if (Object(value) === value) // then it is an object; i.e., a non-primitive

0
在JavaScript中,null表示“无”。它应该是一个不存在的东西。不幸的是,在JavaScript中,null的数据类型是一个对象。你可以认为typeof null是一个JavaScript的bug。它应该是null。

0

这是来自 JavaScript 第一个版本的一个 bug 残留。

“这是一个 bug,不幸的是它无法修复,因为这会破坏现有的代码。”

参考资料和更多信息:https://2ality.com/2013/10/typeof-null.html


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接