在JavaScript中,我们经常需要判断一个变量或一个值的数据类型,以便进行相应的操作或处理。JavaScript提供了多种类型判断的方法,但是它们各有优劣,不同的场景需要选择合适的方法。本文将介绍JS类型判断的三种常用方法:typeof、instanceof和Object.prototype.toString,并分析它们的原理和适用范围。
typeof
typeof是一个一元操作符,它接受一个操作数,返回一个表示操作数类型的字符串。例如:
typeof 1 // "number"
typeof "hello" // "string"
typeof true // "boolean"
typeof undefined // "undefined"
typeof null // "object"
typeof {} // "object"
typeof [] // "object"
typeof Symbol() // "symbol"
typeof可以正确地判断基本数据类型(number、string、boolean、undefined和symbol),但是对于null和引用数据类型(对象、数组、函数等),它都返回object,无法区分具体的子类型。这是因为在JavaScript的最初版本中,使用的是32位系统,为了性能考虑,使用低位存储了变量的类型信息,000开头代表是对象,然而null表示为全零,所以被误判为object。虽然后来JavaScript的内部类型判断标准改为使用[[Class]]属性,但是为了兼容之前的代码,typeof对null的判断仍然保持不变。
typeof的优点是简单易用,对于基本数据类型的判断很准确,而且可以检测出函数类型。缺点是不能判断null和引用数据类型的具体子类型。
instanceof
instanceof是一个二元操作符,它接受两个操作数,左边是一个对象,右边是一个构造函数或者类。它的功能是判断左边的对象是否是右边的构造函数或类的实例。例如:
[] instanceof Array // true
[] instanceof Object // true
{} instanceof Object // true
new Date() instanceof Date // true
function a() {}
a instanceof Function // true
new a() instanceof a // true
可以看出,instanceof可以正确地判断引用数据类型的子类型,但是它也有一些问题。首先,它不能判断基本数据类型,因为基本数据类型不是对象。其次,它只能判断对象和构造函数之间的关系,而不能判断对象具体属于哪种类型。再次,它受原型链的影响,如果修改了原型链,可能导致判断结果不准确。最后,它不能处理跨执行上下文(iframe或window)的情况,在不同的执行上下文中创建的对象彼此不共享原型链。
instanceof的优点是能够判断引用数据类型的子类型,并且考虑了继承关系。缺点是不能判断基本数据类型和null,并且受原型链和执行上下文的影响。
Object.prototype.toString
Object.prototype.toString是Object原型对象上的一个方法,它返回一个表示对象类型的字符串,格式为[object Xxx],其中Xxx就是对象的具体子类型。这个方法实际上是调用了对象内部的[[Class]]属性来返回结果。例如:
Object.prototype.toString.call(1) // "[object Number]"
Object.prototype.toString.call("hello") // "[object String]"
Object.prototype.toString.call(true) // "[object Boolean]"
Object.prototype.toString.call(undefined) // "[object Undefined]"
Object.prototype.toString.call(null) // "[object Null]"
Object.prototype.toString.call({}) // "[object Object]"
Object.prototype.toString.call([]) // "[object Array]"
Object.prototype.toString.call({}) // "[object Object]"
Object.prototype.toString.call(function(){}) // "[object Function]"
Array.isArray()
Array.isArray() 方法用于判断一个变量是否为数组类型,它可以准确地判断数组类型,但不能判断其他类型。
Array.isArray([]) // true
Array.isArray({}) // false
总结
这边提供一个通用的类型判断方法:
function getType(obj) {
const type = typeof obj;
if (type !== "object") {
return type;
}
if (Array.isArray(obj)) {
return "array";
}
if (obj === null) {
return "null";
}
return Object.prototype.toString.call(obj).slice(8, -1).toLowerCase();
}
console.log(getType("hello")); // 输出: "string"
console.log(getType(123)); // 输出: "number"
console.log(getType([1, 2, 3])); // 输出: "array"
console.log(getType({ name: "Tom", age: 18 })); // 输出: "object"
console.log(getType(null)); // 输出: "null"
console.log(getType(undefined)); // 输出: "undefined"