最稳的类型判断方式是object.prototype.tostring.call(),它直接读取对象内部类型标识,不受构造函数、原型链或全局环境影响,能准确区分null、数组、正则等所有内置类型。

最稳的类型判断方式,就是直接读对象底层的类型标识,不看构造函数、不走原型链、不依赖全局环境。
为什么它比 typeof 和 instanceof 更可靠
typeof 对很多值都返回 "object":null、数组、正则、日期、Map、Set……全被混为一谈。它连 null 和普通对象都分不清,更别说区分 Array 和 Date 了。
instanceof 依赖原型链和构造函数,在跨 iframe、微前端等多全局环境下会失效;对原始值(如字面量字符串、数字)也不起作用。
而 Object.prototype.toString.call() 返回的是标准格式字符串,比如:
- Object.prototype.toString.call([]) → "[object Array]"
- Object.prototype.toString.call(/a/g) → "[object RegExp]"
- Object.prototype.toString.call(null) → "[object Null]"
- Object.prototype.toString.call(undefined) → "[object Undefined]"
- Object.prototype.toString.call(new Date()) → "[object Date]"
- Object.prototype.toString.call(BigInt(1)) → "[object BigInt]"
核心原理:读取内部类型槽位
每个内置对象在引擎内部都有一个类型标识,ES5 叫 [[Class]],ES6+ 后演变为 [[NativeBrand]] 或受 Symbol.toStringTag 控制。Object.prototype.toString 的原生实现会直接读这个标识,拼成 "[object Xxx]" 格式返回。
这个过程是引擎特权,JS 代码无法伪造或绕过——哪怕你重写了对象自己的 toString 方法,也不会影响它。
封装一个实用的 typeOf 函数
每次都写一长串太麻烦,推荐封装成简洁工具函数:
const typeOf = val => {
const raw = Object.prototype.toString.call(val);
return raw.slice(8, -1).toLowerCase();
};
使用示例:
- typeOf([1, 2]) → "array"
- typeOf(new Map()) → "map"
- typeOf(Promise.resolve()) → "promise"
- typeOf(Symbol('x')) → "symbol"
- typeOf(null) → "null"
注意 Symbol.toStringTag 的影响
ES6 允许对象自定义类型标签:
class MyType {
get [Symbol.toStringTag]() { return 'MyType'; }
}
Object.prototype.toString.call(new MyType()); // "[object MyType]"
这能让 typeOf 返回 "mytype",适合语义化扩展;但也要清楚——它可被任意设置,不具备安全性,仅适用于可控场景。未设置时,自定义实例仍返回 "object"。











