JavaScript写一个返回数据类型的函数

6 阅读2分钟

获取数据类型方法

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,而且也不能区分基本类型stringboolean。可以左边放你要判断的内容,右边放类型来进行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()

上面我们通过typeofinstanceof实现了一版类型判断,那么是否有其他渠道,使我们的代码更加简洁吗?答案就是使用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;
}

优势

  1. 精准度高:修复了 typeof 的 3 个核心缺陷(null 误判为 object、数组误判为 objectNaN 误判为 number 但合理识别);
  2. 覆盖全面:支持 ES6+ 新增类型(SymbolBigIntMapSetPromise)和 DOM 元素、错误对象等特殊类型;
  3. 性能优异:基础类型直接用 typeof(高效),仅引用类型用 Object.prototype.toString.call()(标准化);
  4. 使用简单:返回值为小写字符串,统一格式,便于后续判断(如 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
};