引言
首先,js的数据类型分为原始类型和引用类型,如下。
js数据类型一共有八种,原始类型有七种,而object,array,function,Date统称为引用类型一种。有时也可将number和bigint统称为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);
结果输出:
JavaScript 中,几乎所有的东西都是对象,或者可以看作是对象的变种,原型链的顶端也是Object。很多对象都通过原型链与 Object.prototype 相关联,Array、Function、Date 等都会继承 Object.prototype 里面的方法。
让我们再看一段代码
发现输出也是true。定义了一个构造函数Bus,利用new获得了一个示例对象bus,而在Bus的原型上又增加了一个构造函数Car,这就相当于
所以说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
}
通过测试,代码正确。
三. Object.prototype.toString.call(x)
Object.prototype.toString.call(x)这个方法就稍微强大一点了,既可以判断原始数据类型,又可以判断引用数据类型,如下。
其中call就是让后面的x短暂拥有Object上原型上的toString方法,也就相当于执行x.toString()。
那么其执行原理是怎么样的呢?我们先来查看官方文档;
使用
.call就让此时的this指向了x。
前两条的意思如下;
第三条开始将this指向的值也就是此时的 x 传给ToObject调用,执行ToObject(x)操作,定义一个class,赋值为x的[[Class]]内部属性的值也就是x的数据类型,此时class就是x的数据类型,然后返回[Object class]样子的字符串。其实就是读取了数据内定的一个属性[[Class]],里面存放着数据类型。
四. Array.isArray(x)
这个就是专门用于判断数组的类型,也只有数组可以用。
包装类
知道了instanceof原理,我们再来看以下代码;
console.log(new String('hello') instanceof String); //true
console.log('hello' instanceof String); //false
为什么会这样呢?这样就要引出包装类的概念了,包装类是指能够将原始数据类型转换为对象的一层“包装”,也就是通过new出来的对象。不用new生成的原始数据类型会默认它是一个字面量,是不能拥有属性和方法的,而用new生成的就会认为是一个对象,是有属性和方法的,如下图。
总结
-
typeof 可以准确的判断除了 null 之外的所有原始类型,不能判断引用类型(除了 function)
-
instanceof 通过原型链来判断类型相等,只能判断引用类型(原始类型没有隐式原型)
-
Object.prototype.toString.call(x) 借助Object原型上的toString方法在执行过程中会读取 x 的内部属性[[class]]这一机制
-
Array.isArray() 数组独有的判断方法。