为什么typeof判断null为object

4 阅读2分钟

这是 JavaScript 中最著名的设计错误和历史遗留问题typeof null === "object" 并不是逻辑上的正确行为,而是一个Bug

原因分析

1. 历史背景

在 JavaScript 最初的实现(1995年)中,JavaScript 中的值是用一个类型标签和实际值表示的:

  • 对象的类型标签是 0
  • null 在大多数平台被表示为全零的指针:0x00
  • 当检查 null 的类型标签时,得到的是 0,也就是对象的标签

2. 内部表示

JavaScript 引擎内部使用 32位 存储值:

  • 低 1-3 位存储类型标签
  • 000:对象
  • 1:整数
  • 010:双精度浮点数
  • 100:字符串
  • 110:布尔值

null 在内存中通常表示为全零(空指针),所以类型标签也被认为是 000(对象)。

为什么不能修复?

实际上,ECMAScript 委员会曾考虑过修复这个问题,但最终决定不修复,原因:

向后兼容性

// 假设有大量现有代码:
if (typeof something === "object") {
  // 可能处理了 null 情况
}

// 如果 typeof null 变成 "null",这些代码会出错

权衡取舍

修复这个 Bug 会:

  1. 破坏大量现有网站和代码
  2. 引入新的不一致性
  3. 收益有限(只是让一个操作符更正确)

如何正确检测 null?

既然 typeof 不可靠,我们需要其他方法:

1. 使用严格相等

value === null

2. 同时检查 null 和 undefined

// 通常我们想一起检查 null 和 undefined
value == null  // true 当 value 是 null 或 undefined
value === null || value === undefined

3. 使用 Object.prototype.toString

Object.prototype.toString.call(null)  // "[object Null]"

4. 使用现代 JavaScript 特性

// ES2020 可选链 + nullish 合并
value ?? 'default'

// 类型守卫
function isNull(value: unknown): value is null {
  return value === null
}

一个更准确的类型检查函数

function betterTypeOf(value) {
  if (value === null) {
    return 'null';
  }
  if (value === undefined) {
    return 'undefined';
  }
  const type = typeof value;
  if (type === 'object') {
    if (Array.isArray(value)) {
      return 'array';
    }
    if (value instanceof Date) {
      return 'date';
    }
    if (value instanceof RegExp) {
      return 'regexp';
    }
  }
  return type;
}

// 测试
betterTypeOf(null)        // "null"
betterTypeOf([])          // "array"
betterTypeOf({})          // "object"
betterTypeOf(new Date())  // "date"

实际影响

这个 Bug 告诉我们几个重要经验:

  1. 语言设计要谨慎:早期的决定可能影响几十年
  2. 永远不要完全信任类型检查:总是要多重验证
  3. 理解原理很重要:知道 typeof 的局限性

总结

  • typeof null === "object" 是一个历史 Bug
  • 原因是 JavaScript 内部使用类型标签,而 null 的标签被错误地标记为对象
  • 由于兼容性问题,这个 Bug 永远不会被修复
  • 在实际开发中,使用 === null 来准确检测 null
  • 这是一个很好的面试题,考察对 JavaScript 底层原理的理解