object.prototype.tostring 的核心能力是返回对象真实的内部类型标识,它通过读取对象的[[class]]内部属性(es5)或symbol.tostringtag(es6+)生成"[object xxx]"格式字符串,且必须用call/apply显式绑定this才能正确识别类型,对null和undefined有特殊处理。

Object.prototype.toString 的核心能力,是返回对象真实的内部类型标识,而不是表层表现。它不依赖原型链上被重写的 toString 方法,也不受构造函数名称或自定义逻辑干扰——靠的是 JavaScript 引擎为每个对象内置的、不可篡改的类型标记机制。
它读取的是对象的 [[Class]] 内部属性
在 ES5 及更早规范中,每个对象都有一个隐藏的 [[Class]] 内部属性,值为字符串(如 "Array"、"Date"、"RegExp")。这个属性由引擎在创建对象时自动设定,无法通过代码直接读取或修改。
Object.prototype.toString 的默认行为,就是提取并格式化这个 [[Class]] 值,拼成 "[object XXX]" 字符串。例如:
-
[]的 [[Class]] 是 "Array" →"[object Array]" -
new Date()的 [[Class]] 是 "Date" →"[object Date]" -
new Map()的 [[Class]] 是 "Map" →"[object Map]" - 普通对象
{}的 [[Class]] 是 "Object" →"[object Object]"
ES6 起:[[Class]] 升级为 Symbol.toStringTag 控制逻辑
ES6 引入了 Symbol.toStringTag,作为 [[Class]] 的可编程替代。Object.prototype.toString 在执行时会优先检查该 symbol 属性:
- 若对象自身或原型链上有
toStringTag且值为字符串,则用该值作为类型名 - 否则回退到内置规则(如检测是否有 [[Call]] 内部方法 → 判为 "Function";是否有 [[DateValue]] → 判为 "Date")
这解释了为什么你可以“伪造”类型识别:
const fakeArray = { [Symbol.toStringTag]: 'Array' };Object.prototype.toString.call(fakeArray); // "[object Array]"
为什么必须用 call/apply?
因为 Object.prototype.toString 是通用方法,它的逻辑完全基于 this 的值。如果不指定 this,调用就失效:
-
{}.toString()调用的是Object.prototype.toString—— ✅ 返回 "[object Object]" -
[].toString()调用的是Array.prototype.toString—— ❌ 返回 "1,2,3"(已重写) -
Object.prototype.toString()中 this 指向 Object.prototype —— ❌ 返回 "[object Object]"(无意义) -
Object.prototype.toString.call([])把 this 显式绑定为数组实例 —— ✅ 返回 "[object Array]"
对 null 和 undefined 的特殊处理
这两个值不是对象,无法被 ToObject 转换。规范明确约定:
- 当 this 是
null→ 直接返回"[object Null]" - 当 this 是
undefined→ 直接返回"[object Undefined]"
这是唯一不需要装箱(boxing)就能准确识别它们的方式 —— typeof 虽能识别 undefined,但对 null 返回 "object",而 instanceof 完全无效。











