在日常的开发过程中,我们经常需要验证某个变量或参数所代表的数据的类型,以保证程序仅处理类型相符的数据,避免错误。

自 ES6 发布之后,JavaScript 中的数据类型又多了几种,但判断的方式基本还是一样的。

这篇文章就来温习一下 JavaScript 中判断数据类型的方式。

数据类型

JavaScript 中的数据类型分为两类,一类是原始类型,包括 StringNumberBooleanNullUndefinedSymbolBigInt;另一类是引用类型,包括:ArrayMapSetWeakMapWeakSetJSONRegExpDateFunctionObject

判断方法

JavaScript 中常见的判断数据类型的方法有四种(个人认为两种有效的只有两种),它们各有优缺点。

typeof

typeof 在判断数据类型时,会以全小写字符串的形式返回判断结果,如下:

console.log(typeof 'qingcong')                    // string
console.log(typeof 123)                           // number
console.log(typeof true)                          // boolean
console.log(typeof null)                          // object
console.log(typeof undefined)                     // undefined
console.log(typeof Symbol('x'))                  // symbol
console.log(typeof BigInt(100))                  // bigint
console.log(typeof [])                           // object
console.log(typeof new Map())                    // object
console.log(typeof new WeakMap())               // object
console.log(typeof new Set())                    // object
console.log(typeof new WeakSet())               // object
console.log(typeof new RegExp(/\d/))            // object
console.log(typeof new Date())                  // object
console.log(typeof new Function())              // function
console.log(typeof new Object({}))              // object

可以看到,对于原始类型,除了 null 返回的是 object 外,其余判断都是正确的;而对于引用类型来说,除了 Function 判断正确外,其余的都返回了 object

因此,typeof 可以用来判断原始类型和 Function,但对于其它引用类型的数据 typeof 无能为力。

instanceof

由于 typeof 的限制性,JavaScript 中还提供了 instanceof 操作符,但严格来说,instanceof 并不是一种判断数据类型的方法,它检测的是构造函数的 prototype 属性是否出现在某个实例对象的原型链上。

例如:

function Car(make, model, year) {
  this.make = make;
  this.model = model;
  this.year = year;
}

function Plane(make, model, year) {
  this.make = make;
  this.model = model;
  this.year = year;
}

let myCar = new Car("Honda", "Accord", 1998);
console.log(myCar instanceof Car);      // true
console.log(myCar instanceof Plane);    // false

不过也正是因为原型链的缘故,instanceof 在判断时会出现偏差,例如:

console.log(myCar instanceof Object);   //true

因为 Object 也在 myCar 的原型链上。

如果修改了实例对象的原型,那么结果也会不一样:

Object.setPrototypeOf(myCar, Plane.prototype);

console.log(myCar instanceof Car);      // false
console.log(myCar instanceof Plane);    // true

同时,因为是检测实例对象的原型链,所以下面的这两种形式都是无效的:

let stringA = "A simple string";
let stringB = String("A string made with constructor");

console.log(stringA instanceof String)    // false
console.log(stringB instanceof String)    // false
console.log(stringA instanceof Object)    // false
console.log(stringB instanceof Object)    // false

必须是通过 new 创建的实例对象才可以:

let stringC = new String("A string made with new");

console.log(stringC instanceof String)    // true
console.log(stringC instanceof Object)    // true

查看三者的结构,就会发现区别:

struct

使用 typeof 检测可以得出:

console.log(typeof stringA)   // string
console.log(typeof stringB)   // string
console.log(typeof stringC)   // object

这就是本节开头所说的,instanceof 并不是一种判断数据类型的方法的原因。

constructor

此方法是通过判断对象的构造器来判断其数据类型的,它并不是由 JavaScript 直接提供的一种判断方法,而是一种变通的方法,本质上和 instanceof 的判断方式是相同的,也是以原型链的形式进行。

所以,instanceof 方法的缺点它都有,这里就不展开了。

toString

toString 方法是 Object 的原型方法,也是一种判断数据类型的变通方法,它会返回一个 [object xxxx] 的形式的字符串,其中 xxxx 就是对象的类型。

例如:

console.log(Object.prototype.toString.call("qingcong"));               // [object String]
console.log(Object.prototype.toString.call(123));                      // [object Number]
console.log(Object.prototype.toString.call(true));                     // [object Boolean]
console.log(Object.prototype.toString.call(null));                     // [object Null]
console.log(Object.prototype.toString.call(undefined));                // [object Undefined]
console.log(Object.prototype.toString.call(Symbol("x")));              // [object Symbol]
console.log(Object.prototype.toString.call(BigInt(100)));              // [object BigInt]
console.log(Object.prototype.toString.call([]));                        // [object Array]
console.log(Object.prototype.toString.call(new Map()));                 // [object Map]
console.log(Object.prototype.toString.call(new WeakMap()));             // [object WeakMap]
console.log(Object.prototype.toString.call(new WeakSet()));             // [object WeakSet]
console.log(Object.prototype.toString.call(new RegExp(/\d/)));          // [object RegExp]
console.log(Object.prototype.toString.call(new Date()));                // [object Date]
console.log(Object.prototype.toString.call(new Function()));            // [object Function]
console.log(Object.prototype.toString.call(new Object()));              // [object Object]

可以看到,此方法的判断非常准确。

结语

尽管在日常开发中,我们并不会真的需要判断每一种数据类型,但判断的方法是通用的,掌握了方法,就能从容应对各种需求。

还有一点要说的是,ES6 提供了一个 Array.isArray() 方法专门用于检测某个变量是否是数组类型,在日常开发中也可以使用。

最近更新:
作者: MeFelixWang