获取数据类型方法
typeof
由于由于历史原因,在判断原始类型时,typeof null会等于object。而且对于对象(Object)、数组(Array)来说,都会转换成object。例子如下:
typeof 1 // 'number'
typeof "1" // 'string'
typeof null // 'object'
typeof undefined // 'undefined'
typeof [] // 'object'
typeof {} // 'object'
typeof function() {} // 'function'
所以我们可以发现,typeof可以判断基本数据类型,但是难以判断除了函数以外的复杂数据类型。
instanceof
instanceof是通过原型链来判断的,但是对于对象来说,Array也会被转换成Object,而且也不能区分基本类型string和boolean。可以左边放你要判断的内容,右边放类型来进行JS类型判断,只能用来判断复杂数据类型,因为instanceof 是用于检测构造函数(右边)的 prototype 属性是否出现在某个实例对象(左边)的原型链上。例如:
function Func() {}
const func = new Func()
console.log(func instanceof Func) // true
const obj = {}
const arr = []
obj instanceof Object // true
arr instanceof Object // true
arr instanceof Array // true
const str = "abc"
const str2 = new String("abc")
str instanceof String // false
str2 instanceof String // true
单独使用instanceof好像也是不行的,但是我们对于typeof已经得出结论,不能区分数组和对象,那么,我们结合下instanceof,来写一个完整的判断逻辑
function myTypeof(data) {
const type = typeof data
if (data === null) {
return 'null'
}
if (type !== 'object') {
return type
}
if (data instanceof Array) {
return 'array'
}
return 'object'
}
Object.prototype.toString.call()
上面我们通过typeof和instanceof实现了一版类型判断,那么是否有其他渠道,使我们的代码更加简洁吗?答案就是使用Object.prototype.toString.call()。
每个对象都有一个toString()方法,当要将对象表示为文本值或以预期字符串的方式引用对象时,会自动调用该方法。默认情况下,从Object派生的每个对象都会继承toString()方法。如果此方法未在自定义对象中被覆盖,则toString()返回[Object type],其中type是对象类型。所以就有以下例子:
Object.prototype.toString.call(new Date()) // [object Date]
Object.prototype.toString.call("1") // [object String]
Object.prototype.toString.call(1) // [object Numer]
Object.prototype.toString.call(undefined) // [object Undefined]
Object.prototype.toString.call(null) // [object Null]
封装以下通用类型判断方法:
function myTypeof(data) {
var toString = Object.prototype.toString;
var dataType = data instanceof Element ? "Element" : toString.call(data).replace(/[object\s(.+)]/, "$1")
return dataType
};
myTypeof("a") // String
myTypeof(1) // Number
myTypeof(window) // Window
myTypeof(document.querySelector("h1")) // Element
或者
/**
* 精准检测 JavaScript 数据类型
* @param {any} value - 待检测的值(任意类型)
* @returns {string} 类型名称(小写,如 'string'、'array'、'map')
*/
function getDataType(value) {
// 处理 null 特殊情况(Object.prototype.toString.call(null) 返回 "[object Null]")
if (value === null) {
return 'null';
}
// 基础类型(string/number/boolean/undefined/symbol/bigint)直接用 typeof
const baseType = typeof value;
if (!['object', 'function'].includes(baseType)) {
return baseType;
}
// 引用类型(对象/数组/函数/Map/Set 等)用 Object.prototype.toString.call()
const typeStr = Object.prototype.toString.call(value);
// 截取类型字符串(如 "[object Array]" → "Array"),转为小写
const exactType = typeStr.slice(8, -1).toLowerCase();
return exactType;
}
优势
- 精准度高:修复了
typeof的 3 个核心缺陷(null误判为object、数组误判为object、NaN误判为number但合理识别); - 覆盖全面:支持 ES6+ 新增类型(
Symbol、BigInt、Map、Set、Promise)和 DOM 元素、错误对象等特殊类型; - 性能优异:基础类型直接用
typeof(高效),仅引用类型用Object.prototype.toString.call()(标准化); - 使用简单:返回值为小写字符串,统一格式,便于后续判断(如
if (getDataType(val) === 'array') {})。
扩展场景
如果需要更细致的类型判断(如区分 “普通对象” 和 “自定义类实例”),可扩展如下:
// 扩展:判断是否为“纯对象”(排除数组、Map、Set 等,仅 {}/new Object())
function isPlainObject(value) {
return getDataType(value) === 'object' && value.constructor === Object;
}
// 测试
console.log(isPlainObject({})); // true
console.log(isPlainObject(new Person())); // false(自定义类实例)
console.log(isPlainObject([])); // false(数组)
获取实例化对象的类名
对于上述获取的 Object 类型的数据,直接使用 xx.constructor.name 即可获取到这个数据对应的类名。
function myTypeof(data) {
var toString = Object.prototype.toString;
var dataType = data instanceof Element ? "Element" : toString.call(data).replace(/[object\s(.+)]/, "$1")
if(dataType === 'Object'){
return data.constructor.name
}
return dataType
};