JavaScript数据类型检测方法总结

本文介绍了JavaScript中几种数据类型检测方法。typeof根据二进制计算返回类型,对基础类型检测快,但对对象类型不友好;instanceof用于检测实例所属类,可细分对象类型,但有无法检测原始值等缺陷;constructor可获取对象构造函数,但结果不一定准确;Object.prototype.toString.call()是推荐方案,能准确返回类型信息。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

typeof

返回一个字符串,包含了对应的数据类型。

typeof null // object
typeof new Number(10) // object
// typeof 不能细分对象
// 非null的原始值 和 函数类型的检测很友好 

原理:根据计算机底层存储值的二进制来计算。性能会好一些。

  • 对象类型监测不友好,基础类型除了null都可以快速检测。
  • 000开始的值都被认为是对象类型,而null的值是000000,所以被认为是对象。
  • Function 类型判断是否实现了call 方法,有就是function 类型

instanceof

  • 本意不是检测数据类型,而是检测当前实例是不是属于这个类。

  • 可以根据instanceof 细分对象类型。

[] instanceof Array // true
[] instanceof Object // true 

原理:首先按照构造函数的Symbol.hasInstance([实例]) 方法进行检测,如果存在这个属性方法,则方法执行返回的值就是最后检测结果。如果不存在这个属性方法,则会查找当前实例的原型链(一直找到Object.prototype为止),如果查找中途,找到了某个原型等于构造函数的原型「构造函数出现在实例的原型链上」则返回结果为true,反之是false。

  • 缺陷:

    • 无法检测原始值类型(不会像隐式调用一样有装箱操作)
    • 如果原型重定向后此方法就无法正确找到原型
10 instanceof Number // false
​
function Fn() {}
Fn.prototype = [];
let f = new Fn();
f instanceof Array // true 
// es6 可以重写Symbol.hasInstance方法,es5不可以
class Fn {
  static [Symbol.hasInstance](value) {
    return true;
  }
}
1 instanceof Fn // true 
function instance_of(obj, Ctor) {
  // 数据格式校验
  if (Ctor == null || Ctor == undefined) {
    throw new TypeError('Right-hand side of "instanceof" is not an object');
  };
  if (!Ctor.prototype) {
    throw new TypeError('Malformed arrow function parameter list');
  }
  if (typeof Ctor !== 'function') {
    throw new TypeError('Right-hand side of "instanceof" is not callable');
  }
  
  // 原始值类型忽略
  if (obj == null) return false;
  if (!/^(object|function)$/.test(typeof obj)) return false;
  
  // 检测是否有Ctor[Symbol.hasInstance]方法
  if (typeof Ctor[Symbol.hasInstance] === 'function') {
    return Ctor[Symbol.hasInstance](obj);
  }
  
  // 走到最后按照原型链查找
  let prototype = Object.getPrototypeOf(obj);
  while(prototype) {
    if (prototype === Ctor.prototype) return true;
    prototype = Object.getPrototypeOf(prototype);
  }
  return false;
} 

constructor

  • 一个获取对象的构造函数,不是用来专门判断数据类型的。

  • 优点:

    • 为true的值至多只有一个
    • 原始值调用时会进行“装箱”(除了 null 和 undefined)
  • 缺点

    • constructor是可以被随意更改的,检测结果不一定准确
[].constructor === Array // true
[].constructor === Object // false 

Object.prototype.toString.call()

最为推荐的一种方案。

原理:首先找到Object.prototype.toString方法,把toString执行之后,让方法中的this变为要检测的值,toString内部会返回对应this的数据类型信息。

返回值:‘[object Object]’

大部分值所属类的原型上都有toString方法,除了Object.prototype.toString 是用来检测类型的,其余的一般都是用来转为字符串的。

非普通对象.toString 一般先调取自己所属类原型上的toString

普通对象.toString 会直接调取Object.prototype.toString

({}).toString() // [object Object]
Object.prototype.toString.call({}) // [object Object]
(1).toString() // '1' 

步骤:首先看[value] [Symbol.toStringTag],如果存在这个属性,属性值是啥,返回的类型就是啥,如果没有这个属性,一般是返回所属的构造函数信息

不同场景下的类型检测

var class2type = {}; // Object
var getProto = Object.getPrototypeOf;
var toString = class2type.toString; // Object.prototype.toString
var hasOwn = class2type.hasOwnProperty; // Object.prototype.hasOwnProperty
var fnToString = hasOwn.toString; // Function.prototype.toString
// ObjectFunctionString = 'function Object() { [native code] }'
var ObjectFunctionString = fnToString.call(Object); // Object.toString()
​
var typeArr = ['Boolean', 'Number', 'String', 'Function', 'Array', 'Date','RegExp', 'Object', 'Error', 'Symbol', 'BigInt', 'Map', 'Set']; // ...
typeArr.forEach(function (name) {
  class2type["[object " + name + "]"] = name.toLowerCase();
})
​
function toType(obj) {
  if (obj == null) {
    return obj + "";
  }
​
  return typeof obj === "object" || typeof obj === "function" ?
    class2type[toString.call(obj)] || "object" :
  typeof obj;
}
​
// 使用正则
function toType(obj) {
  if (obj == null) {
    return obj + "";
  }
  if (typeof obj !== "object" || typeof obj !== "function" ) {
    return typeof obj;
  }
  var reg = /^[object ([0-9A-Za-z]+)]$/, value = reg.exec(toString.call(obj))[1] || 'object';
  return value.toLowerCase();
}
​
// 是否是一个函数
function isFunction(obj) {
  // JS中获取object元素,在某些浏览器中,基于typeof检测这个对象,返回的是function
  return typeof obj === "function" && typeof obj.nodeType !== "number" &&
    typeof obj.item !== "function";
};
​
// 是否是window对象
function isWindow(obj) {
  // undefined == null -> true
  return obj != null && obj === obj.window;
};
​
​
// 检测是否为数组和类数组
function isArrayLike(obj) {
  var length = !!obj && "length" in obj && obj.length,
      type = toType(obj);
​
  if (isFunction(obj) || isWindow(obj)) {
    return false;
  }
​
  return type === "array" || length === 0 ||
    typeof length === "number" && length > 0 && (length - 1) in obj;
}
​
// 检测是否为存粹对象(直属类是Object,数组这类不是)
function isPlainObject(obj) {
  var proto, Ctor;
​
  if (!obj || toType(obj) !== 'object') {
    return false;
  }
​
  proto = getProto(obj);
​
  // Objects with no prototype (e.g., `Object.create( null )`) are plain
  if (!proto) {
    return true;
  }
​
  // Objects with prototype are plain iff they were constructed by a global Object function
  Ctor = hasOwn.call(proto, "constructor") && proto.constructor;
  return typeof Ctor === "function" && fnToString.call(Ctor) === ObjectFunctionString;
}
​
// 检测是否是空对象
function isEmptyObject(obj) {
  if (obj == null) {
    return false;
  }
  // Object.getOwnPropertyNames 可以获取不可枚举的key Object.keys 获取可枚举的key
  var keys = Object.getOwnPropertyNames(obj);
  if (typeof Symbol !== 'undefined') {
    keys = keys.concat(Object.getOwnPropertySymbols(obj));
  }
  return keys.length === 0;
}
​
// 是否是数字
function isNumeric(obj) {
  var type = toType(obj);
  return (type === "number" || type === "string") && !isNaN(obj - parseFloat(obj));
}; 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值