js 判断数据类型有哪些方法
判断 JavaScript 数据类型应组合使用 typeof 判断基本类型与函数、Array.isArray 跨 realm 判断数组、Object.prototype.toString.call 兜底判断内置对象、instanceof 判
JavaScript 判断数据类型没有单一万能方法,工程上的标准组合是:typeof 打底判断基本类型、Array.isArray() 单独判数组、Object.prototype.toString.call() 兜底判断内置对象、instanceof 用于自定义类,四者按场景分工使用,任何单一方法都会漏判。
typeof 能判断哪些类型,哪些判断不了?
typeof 的结论是:它能准确返回 8 种字符串结果,但对 null 和所有内置引用类型全部失效。
typeof 返回值为 "undefined"、"boolean"、"number"、"string"、"symbol"、"bigint"、"function"、"object" 之一。也就是说,基本类型(除 null 外)它都能区分,函数也能单独识别出来,但数组、日期、正则、Map、Set、普通对象统统返回 "object",无法再细分。
两个必须记住的历史坑:
typeof null // "object" ← 1995 年 JS 引擎用低位标签存类型,null 的标签全 0 被误判为对象,至今未修
typeof [] // "object"
typeof new Date() // "object"
typeof NaN // "number" ← NaN 也是 number,要判它必须用 Number.isNaN()
结论:typeof 只能用来排除「是不是基本类型 / 是不是函数」,不能用来判断「是不是数组 / 是不是日期」。
instanceof 和 constructor 有什么坑?
instanceof 的结论是:它沿着原型链判断构造器,只对对象有效,且跨 iframe(跨 realm)必然失效。
[] instanceof Array // true
[] instanceof Object // true,因为 Array.prototype 原型链上有 Object.prototype
'abc' instanceof String // false,基本类型没有原型链
跨 realm 失效的例子:在 iframe 里 new Array(),父页面用 instanceof Array 判断会返回 false,因为两边是不同的 Array 构造器。这是 Array.isArray() 存在的根本原因——它在 ES5 中被规定为跨 realm 可靠。
constructor 的坑更明显:obj.constructor 可以被改写,也可以被 Object.create(null) 抹掉,且遇到 null / undefined 直接抛 TypeError。所以 obj.constructor === Array 这种写法只适合内部可控对象,不适合判断外部传入的数据。
Object.prototype.toString.call 为什么最通用?
Object.prototype.toString.call(x) 的结论是:它是判断内置对象类型最通用的方法,返回 [object Type] 格式字符串,能区分 10 种以上内置类型。
const type = x => Object.prototype.toString.call(x).slice(8, -1);
type([]) // "Array"
type(null) // "Null"
type(new Date()) // "Date"
type(/abc/) // "RegExp"
type(new Map()) // "Map"
type(function(){}) // "Function"
type(123) // "Number"
它之所以可靠,是因为它读取的是对象内部的 [[Class]] 内部槽(ES6 后改为读 Symbol.toStringTag),不依赖原型链,跨 realm 也稳定。
一个边界情况必须注意:如果对象自定义了 Symbol.toStringTag,结果会被改写。
const o = { [Symbol.toStringTag]: 'MyType' };
type(o) // "MyType"
所以这个方法对自定义类也不完全可信,用它判断内置类型没问题,判断业务对象仍要配合 instanceof。
实际项目里应该怎么组合判断?
结论:写一个分层判断的 getType 函数,基本类型走 typeof,null 单独拦截,数组走 Array.isArray,其余内置对象走 toString,自定义类走 instanceof,是当前最稳的写法。
function getType(x) {
if (x === null) return 'null';
const t = typeof x;
if (t !== 'object') return t; // undefined/boolean/number/string/symbol/bigint/function
if (Array.isArray(x)) return 'array'; // 跨 realm 可靠
return Object.prototype.toString.call(x).slice(8, -1).toLowerCase();
}
几个高频场景的固定结论:
- 判断数组:只用
Array.isArray(),不要用typeof、instanceof、constructor。 - 判断
NaN:只用Number.isNaN(),x !== x虽等价但可读性差,isNaN()会先做类型转换把"abc"也判成 true。 - 判断普通对象:
type(x) === 'Object',要排除数组和null。 - 判断是否 Promise:
x instanceof Promise在跨库场景不可靠(不同 Promise 实现),改用typeof x?.then === 'function'的鸭子类型判断。 - 判断空对象:
Object.keys(x).length === 0,不要用JSON.stringify(x) === '{}',后者受属性顺序和undefined影响。
总结一下:typeof 负责基本类型和函数,Array.isArray 负责数组,Object.prototype.toString.call 负责其余内置对象,instanceof 只留给自定义类。四者组合而不是互相替代,才是 JS 类型判断的稳定方案;constructor 和裸 instanceof 判断内置类型都应尽量避免。
转载请注明出处,版权归原作者所有。