JavaScript数据类型判断

JS数据类型

值类型(基本数据类型):Number、String、Boolean、Null、Undefined、Symbol、BigInt

引用数据类型(对象类型):Object、Array、Function、RegExp、Data

typeof

typeof 操作符返回一个字符串,表示未经计算的操作数的类型

1
2
typeof operand
typeof(operand)

operand表示对象或原始值的表达式,其类型将被返回

1
2
3
4
5
6
7
8
9
10
11
12
typeof 1 // 'number'
typeof '1' // 'string'
typeof true // 'boolean'
typeof undefined // 'undefined'
typeof Symbol() // 'symbol'
typeof BigInt(number) // 'bigint'
typeof null // 'object'

typeof {} // 'object'
typeof [] // 'object'
typeof console // 'object'
typeof console.log // 'function'

可以看到,typeof能够对基本数据类型进行判断(除了null),无法明确判断引用数据类型

instanceof

instanceof 运算符返回一个布尔值,用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。

1
object instanceof constructor

简单来说,A instanceof B用来判断A是否继承了B

instanceof的原理(判断步骤)如下:

(1)先用typeof判断A,如果A是基本数据类型,则直接返回false。因此instanceof无法很好的判断基本数据类型

(2)如果A不是基本数据类型,则通过A的原型链进行查找,看是否能找到B的显示原型

我们知道,JS对象的隐式原型指向其构造函数的显示原型,因此instanceof就是看B对象的显示原型是否在A对象的(隐式)原型链上,是的话则返回true,若找到原型链尽头也未找到,则返回false。

实现原理如下:

1
2
3
4
5
6
7
8
9
10
11
function myInstanceof(left, right) {
// 这里先用typeof来判断基础数据类型,如果是,直接返回false
if(typeof left !== 'object' || left === null) return false;
// getProtypeOf是Object对象自带的API,能够拿到参数的原型对象,也就是指向对象的隐式原型
let proto = Object.getPrototypeOf(left);
while(true) {
if(proto === null) return false;
if(proto === right.prototype) return true;//找到相同原型对象,返回true
proto = Object.getPrototypeof(proto);
}
}

因此,就有如下判断

1
2
3
4
5
6
7
8
// 定义构建函数
let Car = function() {}
let benz = new Car()
benz instanceof Car // true
let car = new String('xxx')
car instanceof String // true
let str = 'xxx'
str instanceof String // false

toString

toString方法真正指的是Object.prototype.toString.call()

通过调用Object显示原型上的toString方法即可返回对象的数据类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
Object.prototype.toString({})       // "[object Object]"
Object.prototype.toString.call({}) // 同上结果,加上call也ok
Object.prototype.toString.call(1) // "[object Number]"
Object.prototype.toString.call('1') // "[object String]"
Object.prototype.toString.call(true) // "[object Boolean]"
Object.prototype.toString.call(function(){}) // "[object Function]"
Object.prototype.toString.call(null) //"[object Null]"
Object.prototype.toString.call(undefined) //"[object Undefined]"
Object.prototype.toString.call(/123/g) //"[object RegExp]"
Object.prototype.toString.call(new Date()) //"[object Date]"
Object.prototype.toString.call([]) //"[object Array]"
Object.prototype.toString.call(document) //"[object HTMLDocument]"
Object.prototype.toString.call(window) //"[object Window]"

总结

typeof方法可以给出简单数据类型的具体类别,null除外

instanceof方法可以通过原型链判断是否有继承关系返回布尔值

toString方法可以对所有JS对象返回具体的类型

深入探究toString

在JavaScript中,所有的类都继承于Object,因此Object.prototype.toString()方法应该能够被所有对象通过Obj.toString()访问到,并以字符串返回其数据类型

但实际验证结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var num = 123
num.toString() // '123'
var str = 'hello'
str.toString() // 'hello'
var bool = false
bool.toString() // 'false'
var arr = [1, 2, 3]
arr.toString() // '1,2,3'

var obj = {lang:'zh'}
obj.toString() // '[object Object]'
var fn = function(){}
fn.toString() // 'function(){}'

null.toString() // Cannot read property 'toString' of null
undefined.toString() // Cannot read property 'toString' of undefined

可以看到:

简单数据类型toString()方法直接将数据转化为字符串

引用数据类型toString()方法返回其构造函数

NullUndefined没有toString()方法

这是因为JS中各种数据类型不是Object最直接的实例化对象,而是Number、String、Boolean…等等这些类的实例对象。

而这些类在实例化各种数据的时候,改写了toString()方法,因此各数据类型沿原型链使用toString()方法时,到达其构造函数就停止了。

Object.prototype.toString.call() 通过call()改变this指向,使得数据跳过了原型链上的构造函数,直接使用到了Object身上的toString()方法,于是就返回了明确的数据类型


参考文章

面试官:typeof 与 instanceof 区别 | web前端面试 - 面试官系列 (vue3js.cn)

浅谈Object.prototype.toString.call(obj)功能及原理_寒烟说的博客-CSDN博客