JavaScript 有哪些数据类型?如何判断类型?
简化版
JavaScript 数据类型分为原始类型和引用类型。原始类型包括 string、number、boolean、undefined、null、symbol、bigint,引用类型主要是对象。判断类型常用 typeof、instanceof、Object.prototype.toString.call()。
详细版
typeof 适合判断大多数原始类型:
typeof 'a'; // 'string'
typeof 1; // 'number'
typeof undefined; // 'undefined'
typeof Symbol(); // 'symbol'
但它有两个常见坑:
typeof null; // 'object'
typeof []; // 'object'
instanceof 用于判断对象是否出现在某个构造函数的原型链上:
[] instanceof Array; // true
更通用的精确判断方式是:
Object.prototype.toString.call([]); // '[object Array]'
Object.prototype.toString.call(null); // '[object Null]'
面试回答要说明:类型判断没有一个 API 包打天下,要按场景选择。
完整版教学
一、原始类型和引用类型的区别
原始类型的值本身不可再拆分,变量保存的是值。引用类型保存的是对象引用,多个变量可能指向同一个对象。
let a = { count: 1 };
let b = a;
b.count = 2;
console.log(a.count); // 2
这不是“对象赋值会复制对象”,而是两个变量指向同一块对象数据。
二、typeof 为什么不能判断 null 和数组
typeof null === 'object' 是历史遗留问题,不能据此说 null 是对象。数组本质上是对象的一种特殊形式,所以 typeof [] 也是 'object'。
因此 typeof 的合理使用范围是快速判断基本类型和函数:
typeof function () {}; // 'function'
三、instanceof 的边界
instanceof 依赖原型链。它适合判断自定义类实例,但跨 iframe、跨 realm 时可能失效,因为不同运行环境里的 Array 构造函数不是同一个。
value instanceof Array
在普通页面够用,但通用库更倾向 Array.isArray(value)。
四、Object.prototype.toString 的优势
Object.prototype.toString.call(value) 会根据 null/undefined、内置对象品牌以及 Symbol.toStringTag 等规则形成标签,通常能区分数组、日期、正则、null、undefined 等。
它的缺点是写法稍长,实际项目里通常封装成工具函数。对象还能通过 Symbol.toStringTag 改变显示标签,所以它适合常规内置类型识别,却不应被误当成无法伪造的安全类型证明。
五、面试追问与工程落地
面试官常会追问“为什么 typeof null 是 object”。这不是规范认为 null 是对象,而是早期实现里类型标签遗留导致的历史 bug,因为兼容性原因一直保留。回答时要明确:null 是原始类型里的特殊值,表示空对象引用或空值意图,但它本身不是对象。
另一个追问是 Number.isNaN 和全局 isNaN 的区别。全局 isNaN 会先做类型转换,isNaN('abc') 是 true;Number.isNaN 不做隐式转换,只有真正的 NaN 才返回 true。类型判断题里能提到隐式转换,会显得更扎实。
工程里接口数据、表单输入、URL 参数都不要假设类型正确。后端返回的数字可能是字符串,空值可能是 null,也可能字段缺失。类型判断常常要和数据校验结合,尤其是涉及金额、权限、开关配置时。
六、把类型识别与相等语义分开
类型判断回答“值属于什么类别”,相等比较回答“两个值是否按某种规则相同”,不能混为一谈。例如 typeof NaN 是 'number',但 NaN === NaN 为 false;Object.is(NaN, NaN) 为 true。相反,0 === -0 为 true,而 Object.is(0, -0) 为 false。
| 工具 | 适合回答 | 关键边界 |
|---|---|---|
typeof | 原始类型与可调用对象的快速分类 | null 返回 object,普通对象无法细分 |
Array.isArray | 是否为真正数组 exotic object | 跨 realm 仍可靠 |
instanceof | 构造器原型是否出现在对象链上 | 可受 realm 与 Symbol.hasInstance 影响 |
Object.prototype.toString.call | 常见内置对象标签 | Symbol.toStringTag 可影响结果 |
Object.is | SameValue 相等语义 | 它不是类型检测 API |
// 这里列出 7个不同语义的值
const values = [undefined, null, NaN, 0, -0, 1n, Symbol('x')];
values.map(value => typeof value);
// ['undefined', 'object', 'number', 'number', 'number', 'bigint', 'symbol']
ECMAScript 规范明确把 Null 定义为原始类型,同时规定 typeof null 返回字符串 'object'。面试中可以说这是兼容历史行为,但不要把未经规范证明的早期二进制标签故事当成唯一正式解释。
没有一个类型检测 API 能包办所有问题:先问要区分原始值、数组、类实例、跨 realm 内置对象,还是要做运行时数据校验。
七、常见误区与追问
- 误区:
typeof null === 'object'说明 null 是对象。 Null 是独立原始类型,object 只是typeof运算符被规范保留的返回结果。 - 误区:
instanceof只是比较对象的 constructor 属性。 默认算法检查构造器的 prototype 是否出现在对象原型链上,并且可被Symbol.hasInstance自定义。 - 误区:
Object.prototype.toString.call永远无法伪造。 对象可通过Symbol.toStringTag影响标签,因此安全校验不能只依赖显示字符串。 - 追问:为什么跨 iframe 的数组可能让
instanceof Array失败? 不同 realm 拥有不同的 Array 构造器与 prototype,而数组原型链未必包含当前 realm 的Array.prototype。 - 追问:
typeof未声明标识符为什么通常返回 undefined?typeof对不可解析引用有特殊处理;但处于 temporal dead zone 的词法绑定仍可能抛 ReferenceError。 - 追问:
Number.isNaN与全局isNaN有何区别? 前者不做强制类型转换,仅真正的 NaN 为真;后者先转数值,字符串等输入可能产生意外结果。 - 追问:运行时接口数据该怎样校验? 类型探测只能验证局部类别,还应检查字段存在性、范围、联合分支和嵌套结构,必要时使用 schema 校验器。
八、加强记忆
类型题可以按“三把尺”回答:typeof 看基础类型,instanceof 看原型链,Object.prototype.toString 做精确识别。一定要主动提 typeof null 和数组判断,这是高频坑点。