Object.prototype.toString.call
Object.prototype.toString.call 是老派但通用的内建对象精细分类方法,能区分 Date、RegExp、Map、Set 等。
#type / concept
#status / evergreen
#resource / javascript
#resource / ecmascript
[!info] related notes
Object.prototype.toString.call
一句话定义
通过强行调用 Object.prototype 上的 toString 方法,获取值的内部分类标签("[object Type]"),是细分内建对象最通用的方法。
用法
Object.prototype.toString.call([]) // "[object Array]"
Object.prototype.toString.call({}) // "[object Object]"
Object.prototype.toString.call(new Date()) // "[object Date]"
Object.prototype.toString.call(/a/) // "[object RegExp]"
Object.prototype.toString.call(new Map()) // "[object Map]"
Object.prototype.toString.call(new Set()) // "[object Set]"
Object.prototype.toString.call(null) // "[object Null]"
Object.prototype.toString.call(undefined) // "[object Undefined]"
Object.prototype.toString.call(function(){}) // "[object Function]"
原理
每个内建对象都有一个内部分类标识(早期叫 [[Class]],现代通过 Symbol.toStringTag 控制)。
Object.prototype.toString 读取这个标识,返回 "[object Type]" 格式字符串。
为什么要 .call(...)
直接写 [].toString() 调用的是数组自己的 toString(返回元素拼接的字符串),不是我们想要的。
我们想强行调用 Object 原型上的那个版本,所以用:
Object.prototype.toString.call(value)
能区分 null 和 undefined
Object.prototype.toString.call(null) // "[object Null]"
Object.prototype.toString.call(undefined) // "[object Undefined]"
这比 typeof null 靠谱得多。
提取类型名的封装
function getTag(value) {
return Object.prototype.toString.call(value);
}
// 进一步提取
function getType(value) {
return Object.prototype.toString.call(value).slice(8, -1).toLowerCase();
}
getType([]) // "array"
getType(new Date()) // "date"
getType(null) // "null"
Symbol.toStringTag 的影响
现代对象可以自定义标签:
const obj = {
[Symbol.toStringTag]: 'Magic'
};
Object.prototype.toString.call(obj) // "[object Magic]"
所以它也不是绝对不可伪造的。
优点
- 能细分大量内建对象(Array、Date、RegExp、Map、Set、Error 等)
- 连
null/undefined也能区分 - 跨 realm 比
instanceof更稳
缺点
- 写法太长
- 可被
Symbol.toStringTag影响 - 返回的是字符串,需要自己解析
适用场景
- 写通用库时的内建对象细分
- 需要兼容性较强的通用判断逻辑
- 精确区分 Date、RegExp、Map、Set 等