实现一个函数判断数据类型

手写一个精确判断 JavaScript 数据类型的工具函数,支持所有常见数据类型的识别

问题

JavaScript 中的 typeof 操作符存在一些局限性,比如无法区分 null、数组、对象等类型。我们需要实现一个更精确的类型判断函数,能够准确识别所有 JavaScript 数据类型,包括基本类型和引用类型。

解答

/**
 * 精确判断数据类型
 * @param {*} value - 需要判断类型的值
 * @returns {string} - 返回小写的类型字符串
 */
function getType(value) {
  // 处理 null 的特殊情况
  if (value === null) {
    return 'null';
  }
  
  // 处理基本类型
  const baseType = typeof value;
  if (baseType !== 'object') {
    return baseType;
  }
  
  // 处理引用类型,使用 Object.prototype.toString
  const typeString = Object.prototype.toString.call(value);
  // 提取类型名称,如 "[object Array]" => "array"
  const type = typeString.slice(8, -1).toLowerCase();
  
  return type;
}

/**
 * 类型判断的便捷方法集合
 */
const typeUtils = {
  isArray: (value) => getType(value) === 'array',
  isObject: (value) => getType(value) === 'object',
  isString: (value) => getType(value) === 'string',
  isNumber: (value) => getType(value) === 'number',
  isBoolean: (value) => getType(value) === 'boolean',
  isFunction: (value) => getType(value) === 'function',
  isNull: (value) => getType(value) === 'null',
  isUndefined: (value) => getType(value) === 'undefined',
  isSymbol: (value) => getType(value) === 'symbol',
  isDate: (value) => getType(value) === 'date',
  isRegExp: (value) => getType(value) === 'regexp',
  isMap: (value) => getType(value) === 'map',
  isSet: (value) => getType(value) === 'set',
  isPromise: (value) => getType(value) === 'promise',
};

使用示例

// 基本类型
console.log(getType(123));              // "number"
console.log(getType('hello'));          // "string"
console.log(getType(true));             // "boolean"
console.log(getType(undefined));        // "undefined"
console.log(getType(null));             // "null"
console.log(getType(Symbol('id')));     // "symbol"
console.log(getType(100n));             // "bigint"

// 引用类型
console.log(getType([]));               // "array"
console.log(getType({}));               // "object"
console.log(getType(function(){}));     // "function"
console.log(getType(new Date()));       // "date"
console.log(getType(/regex/));          // "regexp"
console.log(getType(new Map()));        // "map"
console.log(getType(new Set()));        // "set"
console.log(getType(Promise.resolve()));// "promise"
console.log(getType(new Error()));      // "error"

// 使用便捷方法
console.log(typeUtils.isArray([1, 2, 3]));        // true
console.log(typeUtils.isObject({}));              // true
console.log(typeUtils.isNull(null));              // true
console.log(typeUtils.isDate(new Date()));        // true
console.log(typeUtils.isPromise(Promise.resolve())); // true

// 实际应用场景
function processData(data) {
  if (typeUtils.isArray(data)) {
    return data.map(item => item * 2);
  } else if (typeUtils.isObject(data)) {
    return Object.keys(data).length;
  } else if (typeUtils.isString(data)) {
    return data.toUpperCase();
  }
  return data;
}

console.log(processData([1, 2, 3]));     // [2, 4, 6]
console.log(processData({a: 1, b: 2}));  // 2
console.log(processData('hello'));       // "HELLO"

关键点

  • Object.prototype.toString.call() - 这是最可靠的类型判断方法,返回格式为 [object Type] 的字符串,能准确识别所有内置类型

  • null 的特殊处理 - typeof null 返回 "object" 是 JavaScript 的历史遗留问题,需要单独判断

  • 字符串处理技巧 - 使用 slice(8, -1) 提取类型名称,去掉 [object ] 部分,并转为小写统一格式

  • 性能优化 - 对于基本类型优先使用 typeof,只有引用类型才使用 Object.prototype.toString,提高判断效率

  • 扩展性设计 - 提供 typeUtils 工具集,封装常用的类型判断方法,使用更便捷,代码更语义化

  • 兼容性好 - 该方法兼容所有现代浏览器和 Node.js 环境,是业界公认的最佳实践