JavaScript类型判断的四种方法

536 阅读4分钟

引言

首先,js的数据类型分为原始类型和引用类型,如下。

image.png

js数据类型一共有八种,原始类型有七种,而objectarrayfunctionDate统称为引用类型一种。有时也可将numberbigint统称为numeric,所以也可称七种。接下来介绍一下判断数据类型的方法有哪些。

一. typeof

typeof是js中一种判断数据类型的方法,它可以准确地判断除了null之外的的所有原始数据类型,不能准确判断引用类型(除function外)。

console.log(typeof 'str');   //string
console.log(typeof 2);    //number
console.log(typeof true);   //boolean
console.log(typeof undefined);    //undefined
let a = Symbol("11")
console.log(typeof a);    //symbol
console.log(typeof 123n);   //bigint
console.log(typeof null);   //object

所有原始数据类型都可以准确地判断它的数据类型(除null外),其中判断null比较特殊,它由于某种原因判断出来是object。(因为在计算机中数据都是以二进制存取的,typeof 判断原理就是判断如果二进制前三位为 0就是 object,(引用类型转换为二进制时前三位都是0),而null64位全为0,所以是object。)

console.log(typeof {});   //object
console.log(typeof []);   //object
console.log(typeof new Date());   //object
console.log(typeof function () { });  //function

引用类型判断都为object(除function外)。function可以判断出为function。

二. instanceof

既然typeof不能判断引用类型,那么肯定就会有能判断引用类型的方法,那就是instanceof。

2.1 instanceof用法和特点

与typeof不同的是,instanof用法是判断一个数据是否属于某一个数据类型,而不是直接输出它是什么类型,如下。

console.log([] instanceof Array);   //true
console.log({} instanceof Object);    //true
console.log(new Date() instanceof Date)   //true
console.log(function () { } instanceof Function);    //true

可以准确判断Array,Object,Date以及Function的类型。

console.log('str' instanceof String);  //false

不能判断原始类型。

console.log([] instanceof Object);   //true
console.log(function () { } instanceof Object);    //true

但是当这样判断的时候,判断出来也是true,这是为什么呢?接下来就要谈到instanceof的原理了。

2.2 instanceof的原理

在js中,万物皆对象,我们将引用类型全部用instanceof判断成Object看看输出是什么;

console.log([] instanceof Object);   
console.log({} instanceof Object);   
console.log(new Date() instanceof Object)   
console.log(function () { } instanceof Object);   

结果输出:

image.png

JavaScript 中,几乎所有的东西都是对象,或者可以看作是对象的变种,原型链的顶端也是Object。很多对象都通过原型链与 Object.prototype 相关联,ArrayFunctionDate 等都会继承 Object.prototype 里面的方法。

让我们再看一段代码

image.png 发现输出也是true。定义了一个构造函数Bus,利用new获得了一个示例对象bus,而在Bus的原型上又增加了一个构造函数Car,这就相当于

image.png 所以说instanceof是通过原型链去判断的,一层一层往上找,找到返回true,反之返回false。

2.3 instanceof源码

既然知道了instanceof是沿着原型链来判断的,那么我们就可以手敲一个与instanceof有相同功能的源码。

定义一个函数myinstanceof,设置俩个形参L,R,通过原型链判断左边是否属于右边。如果能找到相等的即返回true,一直找到原型链顶端还找不到则返回false,具体代码如下所示。

function myinstanceof(L, R) {
  while (L !== null) {
    L = L.__proto__
    if (L === R.prototype) {
      return true
    }
  }
  return false
}

通过测试,代码正确。 image.png

三. Object.prototype.toString.call(x)

Object.prototype.toString.call(x)这个方法就稍微强大一点了,既可以判断原始数据类型,又可以判断引用数据类型,如下。

image.png

其中call就是让后面的x短暂拥有Object上原型上的toString方法,也就相当于执行x.toString()

那么其执行原理是怎么样的呢?我们先来查看官方文档; image.png

image.png 使用.call就让此时的this指向了x

前两条的意思如下;

image.png

第三条开始将this指向的值也就是此时的 x 传给ToObject调用,执行ToObject(x)操作,定义一个class,赋值为x的[[Class]]内部属性的值也就是x的数据类型,此时class就是x的数据类型,然后返回[Object class]样子的字符串。其实就是读取了数据内定的一个属性[[Class]],里面存放着数据类型。

四. Array.isArray(x)

这个就是专门用于判断数组的类型,也只有数组可以用。 image.png

包装类

知道了instanceof原理,我们再来看以下代码;

console.log(new String('hello') instanceof String);  //true
console.log('hello' instanceof String);     //false

为什么会这样呢?这样就要引出包装类的概念了,包装类是指能够将原始数据类型转换为对象的一层“包装”,也就是通过new出来的对象。不用new生成的原始数据类型会默认它是一个字面量,是不能拥有属性和方法的,而用new生成的就会认为是一个对象,是有属性和方法的,如下图。

image.png

总结

  1. typeof 可以准确的判断除了 null 之外的所有原始类型,不能判断引用类型(除了 function)

  2. instanceof 通过原型链来判断类型相等,只能判断引用类型(原始类型没有隐式原型)

  3. Object.prototype.toString.call(x) 借助Object原型上的toString方法在执行过程中会读取 x 的内部属性[[class]]这一机制

  4. Array.isArray() 数组独有的判断方法。