JavaScript 类型判断:几种常用的判断方法

0 阅读4分钟

一、引言

在 JavaScript 开发中,我们经常需要判断一个变量的类型:是字符串还是数字?是数组还是普通对象?是 null 还是 undefined?不同类型的判断需求,对应着不同的方法 —— typeofinstanceofObject.prototype.toString 等。它们各有优缺点和适用场景,本文将从底层原理出发,带大家搞明白三种常用类型判断方法的工作原理,让你在实际开发中做到“因地制宜”。

二、类型回顾

我们如果要进行类型判断,首先我们一定要清楚都有哪些类型:

  • 原始类型:string, number, boolaen, null, undefined, bigint, Symbol
  • 引用类型: Array, function, object, Date

具体的详细类型介绍与各种小tips可以看我往期的文章: juejin.cn/post/763992…

三、typeof

我们先用typeof对所有的常用类型进行打印输出console.log(typeof(s))(s依次替换成num,f,u等等),得到的结果是这个样子的:

let s = 'yunbao'          //string
let num = 463             //number
let f = true              //boolean
let u = undefined         //undefined
let n = null              //object--------这里出问题了!
let sy = Symbol(1)        //symbol
let big = 12345678n       //bigint

let arr = []              //object--------这里也出问题了!
let obj = {}              //object--------存疑,因为我们不知道是否arr出的问题一致
let fn = function () { }  //function

于是我们查阅JS官方文档可知:

  • typeof 是通过将值转为二进制,来判断类型的,二进制前三位是 0 的统一被认为是引用类型,在计算机中,所有的引用类型被转为二进制前三位都是 0(除了函数),而 null 转为二进制是一整串 0 ,因此会出现代码中的那三个特殊情况。

所以:

  1. typeof可以准确的判断除了 null 之外的所有的原始类型
  2. 所有的引用类型在 typeof 眼里都是 object,除了函数

四、instanceof

因为最简单的typeof分辨不出nullArrayobject,我们便找到了第二种方法——instanceof

这种方法比较“呆傻”,只能“你问我答”式,还是用代码一目了然一下,我们用console.log(s instanceof String)依次打印输出:

let s = 'yunbao'          //false
let num = 463             //false
let f = true              //false
let u = undefined         //-------这里报错(undefined is not defined)
let n = null              //-------这里也报错(Null is not defined)
let sy = Symbol(1)        //false
let big = 12345678n       //false

let arr = []              //true
let obj = {}              //true
let fn = function () { }  //true

由此可知: instanceof只能判断引用类型,无法判断原始类型

实际上, instanceof是通过隐式原型链来向上查找 ** 是否隶属于 ** 这个类型的:

  1. 如果arr._ _ proto _ _ === Array.prototype ------- 返回 true
  2. 如果不等于,便继续沿着当前原型的原型链向上查找(也就是取 __proto__.__proto__

Array.prototype._ _ proto _ _ === Object.prototype

  1. 直到向上查找到null,Object.prototype._ _ proto _ _ === null

此时还不相等才会返回false

五、Object.prototype.toString.call()

一步一步到这里我们终于迎来了一个万能的方法来判断数据类型,Object.prototype.toString.call()用其精妙的底层逻辑,巧用this判断所有数据类型。

在聊Object.prototype.toString.call()之前,我们需要普及官方的[[Class]]的相关知识:“es5.github.io/#x15.2.4.2” 这个JS官方文档指出每一个内置对象都有一个内部的 [[Class]] 属性,它的值是一个字符串,用于区分对象的种类,比如 "Array"、"Date"、"Number"、"String" 等。

Object.prototype.toString()的执行步骤

1. 如果 this 值为 undefined,则返回 "[object Undefined]"2. 如果此值为 null,则返回 "[object Null]"3. 否则,我们令O为 调用 ToObject 并将 this 值作为参数传递所得到的结果。 
  // const O = ToObject(this)  这里调用方法不变,this一直指向Object.prototype
  // 因此 O 永远都是 Object

4.class为 O的[[Class]]内部属性的值。
  // const class = O.[[calss]]

5. 返回将三个字符串 "[object "class"]" 拼接后得到的字符串值

此时我们在后面加上.call用来强行扭转this的值指向括号里的内容,使用Object.prototype.toString.call()便可以获取到括号里的参数的[[class]]类型,并且把类型赋值给O,最后再用O.[[calss]]将其调用出来,因此我们便可以正确得到该输入参数的明确类型!

六、扩展与总结

  • typeof 虽然方便,但判断 null 会失真
  • instanceof 能区分引用类型却无法处理原始值
  • Object.prototype.toString.call()什么都可以判断但稍微有些繁琐

因此我们要根据代码所需,选择最合适的判断方法,这样才能提升整段代码的优雅性与健硕性>_<!