1. JS 内部存储值的方式(type tag)
JavaScript 最早期的实现里,每个值在内存里都要有 类型标记(type tag) ,用来告诉 JS 引擎这个值是什么类型。
-
比如:
值类型 内存 type tag number 1 string 2 object 0 boolean 3 ... ... -
JS 引擎执行
typeof x时,并不是去理解x的语义,而是直接查这个 内存里的 type tag。 -
也就是说
typeof早期只是个 低成本内存检查操作。
2. 为什么 null 的 type tag 是 0
-
当时 JS 内部实现
null的方式是:- 它本质上是一个指针,指向 空对象引用(null pointer)。
- 空对象指针的 type tag 被标记为 0,也就是对象。
-
所以:
csharptypeof null // 读取 type tag → 0 → 返回 "object" -
注意,这里不是在问"null 语义上是不是对象",而是引擎只是根据 type tag 决定返回值。
3. 语义 vs 内存实现
| 角度 | 对 null 的理解 |
|---|---|
| 语义 | null 表示"没有对象引用",是原始值,不是对象 |
| 内存实现 | JS 最初实现中,null 用空对象指针存储,type tag=0 |
| typeof 结果 | typeof 直接读取 type tag → "object" |
所以 typeof null === 'object' 是 历史遗留的实现细节,而不是语言逻辑设计的结果。
Object.prototype.toString.call(value)
一、基本用法
javascript
Object.prototype.toString.call(value)
value可以是任意 JS 值(原始值或对象)。- 输出格式:
arduino
"[object Type]"
其中 Type 表示值的内部类型标签 ([[Class]]),比 typeof 更准确。
二、示例
| 值 | typeof |
Object.prototype.toString.call |
|---|---|---|
undefined |
"undefined" | "[object Undefined]" |
null |
"object" | "[object Null]" |
123 |
"number" | "[object Number]" |
"abc" |
"string" | "[object String]" |
true |
"boolean" | "[object Boolean]" |
Symbol() |
"symbol" | "[object Symbol]" |
[] |
"object" | "[object Array]" |
{} |
"object" | "[object Object]" |
function(){} |
"function" | "[object Function]" |
new Date() |
"object" | "[object Date]" |
/regex/ |
"object" | "[object RegExp]" |
Promise.resolve() |
"object" | "[object Promise]" |
new Map() |
"object" | "[object Map]" |
new Set() |
"object" | "[object Set]" |
可以看到,它可以准确区分数组、日期、正则、Map、Set、Promise 等,而
typeof全部都是"object"(除了函数和基本类型)。
三、原理
-
JS 对象都有一个内部属性
[[Class]](在现代规范中叫Symbol.toStringTag):- 这是一个隐藏属性,用来标识对象内部类别。