在 JavaScript 中,准确判断数据类型是开发的基础技能。由于 JS 的动态特性和历史遗留问题,判断类型的方法多种多样,且各有优劣。
以下是目前前端开发中常用的所有判断方式详解。
1. typeof 操作符
typeof 是最基础的判断方式,它可以直接返回一个表示数据类型的字符串。
适用场景 :判断基本数据类型(除了 null)。
语法
javascript
typeof operand
// or
typeof(operand)
例子
javascript
// 基本类型
console.log(typeof 123); // "number"
console.log(typeof 'abc'); // "string"
console.log(typeof true); // "boolean"
console.log(typeof undefined); // "undefined"
console.log(typeof Symbol('id')); // "symbol"
console.log(typeof 10n); // "bigint"
// 引用类型
console.log(typeof function(){}); // "function" (特别注意:函数是唯一能被 typeof 区分出的引用类型)
console.log(typeof {}); // "object"
console.log(typeof []); // "object" (无法区分数组和对象)
console.log(typeof new Date()); // "object"
⚠️ 局限性 (著名的 Bug)
typeof 最大的坑在于判断 null。
javascript
console.log(typeof null); // "object"
这是 JS 初期的设计错误,为了兼容性一直保留至今。因此 不能用 typeof 判断 null。
2. instanceof 操作符
instanceof 用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
适用场景:判断引用类型(对象、数组、正则、日期等),判断自定义类的实例。
语法
javascript
object instanceof constructor
例子
javascript
const arr = [1, 2, 3];
const date = new Date();
const func = function() {};
console.log(arr instanceof Array); // true
console.log(arr instanceof Object); // true (数组也是对象)
console.log(date instanceof Date); // true
console.log(func instanceof Function); // true
// 自定义类
class Person {}
const p = new Person();
console.log(p instanceof Person); // true
⚠️ 局限性
-
无法判断基本数据类型 :
javascriptconsole.log(123 instanceof Number); // false -
原型链被修改后失效 :
javascriptfunction Foo() {} const f = new Foo(); f.__proto__ = {}; // 修改原型指向 console.log(f instanceof Foo); // false -
跨 iframe/window 问题 :
在多个 iframe 之间传递数组时,iframeA的数组instanceof iframeB.Array会返回 false,因为环境不同,构造函数不同。
3. Object.prototype.toString.call() (推荐 🔥)
这是目前公认最准确、最通用 的类型判断方法。它利用了 Object 原型上的 toString 方法,返回格式为 [object Type] 的字符串。
适用场景:所有类型,包括基本类型和引用类型。
语法
javascript
Object.prototype.toString.call(value)
例子
javascript
// 基本类型
console.log(Object.prototype.toString.call(123)); // "[object Number]"
console.log(Object.prototype.toString.call('str')); // "[object String]"
console.log(Object.prototype.toString.call(true)); // "[object Boolean]"
console.log(Object.prototype.toString.call(undefined)); // "[object Undefined]"
console.log(Object.prototype.toString.call(null)); // "[object Null]"
// 引用类型
console.log(Object.prototype.toString.call([])); // "[object Array]"
console.log(Object.prototype.toString.call({})); // "[object Object]"
console.log(Object.prototype.toString.call(new Date())); // "[object Date]"
console.log(Object.prototype.toString.call(/regex/)); // "[object RegExp]"
console.log(Object.prototype.toString.call(function(){})); // "[object Function]"
console.log(Object.prototype.toString.call(Math)); // "[object Math]"
封装通用工具函数
javascript
function getType(value) {
const type = Object.prototype.toString.call(value);
// 截取 "[object Type]" 中的 "Type" 并转小写
return type.slice(8, -1).toLowerCase();
}
console.log(getType([])); // "array"
console.log(getType(null)); // "null"
4. constructor 属性
每个对象实例都有一个 constructor 属性,指向创建该实例的构造函数。
适用场景:判断引用类型,检查数据是由哪个构造函数生成的。
语法
javascript
value.constructor === Constructor
例子
javascript
const arr = [];
const num = 123;
console.log(arr.constructor === Array); // true
console.log(arr.constructor === Object); // false (这点比 instanceof 准确,不回溯原型链)
console.log(num.constructor === Number); // true (数字字面量会自动包装)
console.log((1).constructor === Number); // true
⚠️ 局限性
-
null和undefined没有 constructor ,访问会报错。javascript// null.constructor // TypeError -
不可靠 :如果手动修改了原型的
constructor属性,判断就会出错。javascriptfunction A() {} A.prototype.constructor = Array; // 恶意修改 const a = new A(); console.log(a.constructor === Array); // true (实际类型是 A,判断错误)
5. 特定类型的判断方法
除了上述通用方法,JS 还提供了一些针对特定类型的静态方法。
5.1 Array.isArray()
ES5 新增,专门用于判断数组,解决了 instanceof 在跨 iframe 时的 Bug。
javascript
console.log(Array.isArray([])); // true
console.log(Array.isArray({})); // false
5.2 Number.isNaN()
判断一个值是否严格等于 NaN(Not-a-Number)。注意与全局 isNaN() 的区别。
javascript
// 全局 isNaN 会尝试先转数字,再判断,容易误判
console.log(isNaN("abc")); // true ("abc" 转数字是 NaN)
// Number.isNaN 不会进行类型转换,更严谨
console.log(Number.isNaN("abc")); // false
console.log(Number.isNaN(NaN)); // true
总结与对比
| 方法 | 适用范围 | 优点 | 缺点 |
|---|---|---|---|
| typeof | 基本数据类型 | 语法简单,性能高 | 无法区分 Object、Array、Null |
| instanceof | 引用类型 | 能检测继承关系 | 无法检测基本类型,原型可被修改 |
| Object.prototype.toString | 所有类型 | 最准确,无死角 | 语法稍显繁琐 |
| constructor | 大部分类型 | 简单直接 | 不安全(可被修改),不支持 null/undefined |
| Array.isArray | 仅数组 | 官方标准,解决 iframe 问题 | 仅限数组 |
最佳实践代码
在实际项目中,建议使用以下组合策略:
javascript
function getDataType(data) {
// 1. 优先处理 null
if (data === null) return 'null';
// 2. 处理 undefined (typeof undefined 是安全的)
if (typeof data === 'undefined') return 'undefined';
// 3. 处理基本类型 (除了 null)
if (typeof data !== 'object' && typeof data !== 'function') {
return typeof data;
}
// 4. 使用 toString 处理引用类型 (Array, Date, RegExp, Object, Error 等)
const tag = Object.prototype.toString.call(data);
return tag.slice(8, -1).toLowerCase();
}
// 测试
console.log(getDataType(123)); // "number"
console.log(getDataType(null)); // "null"
console.log(getDataType([])); // "array"
console.log(getDataType(new Date())); // "date"