前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
首页程序员面试题库Object.prototype.toString 类型判断
JavaScript值与类型

怎么用 Object.prototype.toString 做比 typeof 更精确的类型判断?

typeof 对数组、null、正则、日期一律返回 'object',分不出具体类型。Object.prototype.toString.call(value) 会返回 [object Array]、[object Null] 这样的标签,截掉首尾就能拿到类型名。

前端进阶之旅 · 一题精讲更新于 2026.09.29
JavaScript#值与类型
先看核心答案读代码示例
理解线索

标签判断的三个步骤

  1. 借用基类方法通过 call 避开实例重写的 toString
  2. 截取标签从 [object Type] 提取 Type 部分
  3. 识别边界Symbol.toStringTag 可改写显示的类型名

判断数组应优先用 Array.isArray,因为对象可伪造 [object Array] 标签。

核心回答

先记住这个答案

typeof 只能区分原始类型和函数,数组、null、日期、正则全部返回 'object'。Object.prototype.toString 被调用时会返回 [object Xxx] 格式的字符串,Xxx 就是值的内部类型标签,比如数组是 Array、null 是 Null、正则是 RegExp。用法是 Object.prototype.toString.call(value),必须通过 call 把值作为 this 传进去,因为数组、日期这些对象都重写了自己的 toString。拿到结果后 slice(8, -1) 截掉前后缀,就能封装成一个通用的 getType。

  • 用 call 借用基类的 toString
  • [object Type] 可提取内置类型
  • 自定义类通常只得到 Object
  • Symbol.toStringTag 能伪造标签

封装一个通用的 getType

项目里最常见的需求是写一个工具函数,传进任何值都能拿到准确的类型名,用来做参数校验或者深拷贝时的分支判断。

和 typeof 对比着看JavaScript
const toStr = Object.prototype.toString
function getType(value) {
  return toStr.call(value).slice(8, -1) // 去掉 "[object " 和 "]"
}
console.log(typeof [], typeof null, typeof /a/)   // typeof 分不清
console.log(getType([1, 2]), getType(null), getType(/a/))
console.log(getType(undefined), getType(new Date(0)), getType(() => {}))
console.log(getType(new Map()), getType(Promise.resolve()), getType(1n))
console.log([1, 2].toString())                    // 数组自己的 toString
console.log(toStr.call([1, 2]))                   // 基类方法才返回标签
查看输出与解释
object object object
Array Null RegExp
Undefined Date Function
Map Promise BigInt
1,2
[object Array]

'[object ' 正好 8 个字符,所以从下标 8 开始截,-1 去掉最后的 ]。 最后两行说明了为什么一定要 call。数组原型上有自己的 toString,作用是把元素用逗号拼起来,[1, 2].toString() 走的是它,得到 '1,2'。只有直接调用 Object.prototype 上的那个版本,才会返回类型标签。日期也一样,它自己的 toString 返回的是可读的时间字符串。 深拷贝就是典型用法:先用 getType 拿到类型,Array 走数组分支,Date 用 new Date(value) 复制,RegExp 用 new RegExp(value.source, value.flags) 复制,Map 和 Set 逐项复制,其余普通对象递归处理。

标签从哪来,什么时候会不准

标签有两个来源。数组、null、undefined、函数、错误对象、日期、正则这些,是引擎按对象的内部结构直接判断出来的。ES2015 之后新增的 Map、Set、Promise,以及 JSON、Math 这类对象,标签来自它们原型或自身上的 Symbol.toStringTag 属性。而且只要对象有这个属性,就会优先用它,这就带来了两个问题。

自定义类和伪造标签JavaScript
const toStr = Object.prototype.toString
class User {}
console.log(toStr.call(new User()))           // 自定义类只是 Object
class Money {
  get [Symbol.toStringTag]() { return 'Money' }
}
console.log(toStr.call(new Money()))          // 自己定义标签
const fake = { [Symbol.toStringTag]: 'Array' }
console.log(toStr.call(fake), Array.isArray(fake)) // 标签能伪造
console.log(toStr.call(JSON), toStr.call(Math))    // 内置对象自带标签
console.log(toStr.call(function* () {}), toStr.call(async () => {}))
查看输出与解释
[object Object]
[object Money]
[object Array] false
[object JSON] [object Math]
[object GeneratorFunction] [object AsyncFunction]

第一个问题:自定义类的实例默认都是 [object Object],标签法认不出它是 User。判断自定义类型要用 instanceof,或者给类加上 Symbol.toStringTag。 第二个问题:任何对象都能把自己的标签设成 'Array',标签法就被骗了,而 Array.isArray 看的是对象的内部结构,不受影响。所以判断数组一律用 Array.isArray,标签法留给那些没有专用判断方法的类型。 最后一行是个小坑:生成器函数和 async 函数的标签是 GeneratorFunction 和 AsyncFunction,不是 Function。如果 isFunction 写成 getType(v) === 'Function',会漏掉它们,用 typeof v === 'function' 更稳。

跨 iframe 的对象
从 iframe 里拿到的数组,原型是 iframe 自己的 Array.prototype,instanceof Array 会返回 false。标签法和 Array.isArray 不依赖原型链,都能正确识别。这是标签法相比 instanceof 的一个实际优势。
回答前,多想一步

容易答错的地方

直接调用 value.toString()
[1, 2].toString() 返回 '1,2',new Date(0).toString() 返回时间字符串,这些都是子类重写的版本。必须写 Object.prototype.toString.call(value),才能调到返回标签的那个方法。
以为标签是不可篡改的
Symbol.toStringTag 是一个普通的属性,谁都能设置。第三方库给自己的对象加标签是正常用法,但同时意味着标签可以伪造。安全相关的判断要用 Array.isArray 这类基于内部结构的方法。
以为它能认出自定义类
new User() 得到的是 [object Object],和普通字面量对象一样。标签法只对内置类型精确,业务类要用 instanceof。
试着用自己的话回答

面试官还会怎么问?

它和 Array.isArray 判断数组,哪个更可靠?

Array.isArray 更可靠。它直接检查对象是不是数组的内部结构,不读 Symbol.toStringTag,也能识别跨 iframe 的数组。标签法的优势在于通用,一个函数能覆盖日期、正则、Map 等多种类型。

为什么 typeof null 是 'object',而这里能得到 Null?

typeof null 是 JavaScript 第一版实现留下的 bug:当时用低位标记类型,对象的标记是 0,而 null 是空指针,全 0,被误判为对象。为了不破坏已有代码一直没改。Object.prototype.toString 在规范里对 null 和 undefined 做了单独处理,所以能分别返回 [object Null] 和 [object Undefined]。

各种判断类型的方法怎么选?

原始类型和函数用 typeof,但要单独处理 null。数组用 Array.isArray。自定义类用 instanceof。日期、正则、Map 这类内置对象,或者需要一个通用的 getType 时,用 Object.prototype.toString。NaN 用 Number.isNaN。

从一道题,走向一组知识

把知识连起来

值与类型

typeof 和 instanceof 分别在什么场景下用?各有什么坑?

typeof 对数组返回 object,而 instanceof 在跨 realm 时也有局限,可对照各方法的适用范围。

值与类型

为什么 NaN 不等于自身?怎么可靠地判断一个值是不是 NaN?

NaN 的类型是 number,但类型标签无法代替 Number.isNaN 做值判断。

放回知识体系中复习JavaScript 进阶知识

参考资料

  • Object.prototype.toString()

示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。

本题目录
  1. 先记住这个答案
  2. 封装一个通用的 getType
  3. 标签从哪来,什么时候会不准
  4. 容易答错的地方
  5. 面试官还会怎么问
  6. 把知识连起来
读懂,再试着讲出来

先看核心答案,再读代码。最后展开追问,检查自己有没有遗漏边界。

试着回答追问
浏览全部面试题理解原理,也关注真实的使用场景。回到顶部 ↑