新手学前端 JavaScript 类型判断:一篇彻底搞懂 typeof、instanceof 和 Object.prototype.toString

0 阅读5分钟

前言

刚入门 JS 的同学,几乎全都栽在数据类型判断上:想用typeof分辨数组却返回 object,拿instanceof判断基础类型频频翻车,碰到 null、正则、日期更是判断错乱。明明三个判断方法都学过,什么时候用哪个始终分不清,项目里类型校验反复踩坑。本文抛开生硬定义,口语化拆解三种 API 底层逻辑,逐个梳理适用范围、优缺点与易错场景,从原始值、引用对象到特殊内置类型全覆盖。看完不用死记用法,开发时精准选型,一次性搞定 JS 全类型精准判断。

JS类型

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

这些都在往期文章五分钟带你了解js中各种数据类型,让初学者也能轻松记忆前言 在JavaScript的世界里,数据类型是构建一切应用的基石 - 掘金中提到过,读者可自行选择查看。

类型判断方法

1.1 typeof

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

接下来,我们用一段代码给你具体说明举例:

let a = 'hello'
let num = 123
let f = true 
let d = undefined
let n = null   // 返回 object
let sy = Symbol(1)
let big = 1223445326n
let arr = []// 返回 object
let obj = {} // 返回 object
let  fn = function(){}

console.log(typeof fn);
console.log(typeof a);
console.log(typeof num);
console.log(typeof f);
console.log(typeof d);
console.log(typeof n);
console.log(typeof sy);
console.log(typeof big);
console.log(typeof arr);
console.log(typeof obj);

打印结果为:

image.png 我们可以看到,用typeof方法判断变量类型是直接输出其类型,对于原始类型来说,判断可以说非常准确,除了null,对于引用类型,只有function属于 JavaScript 给开发者开的 VIP,可以准确判定,数组和对象输出都是object,这就出现问题了,数组和对象根本区分不出来。那么问题来了,造成上面两种特例的原因是什么呢,这就要从js底层说起。

typeof是通过将值转为二进制,来判断类型的,二进制前三位是 0 的统一被认为是引用类型,在计算机中,所有的引用类型被转为二进制前三维都是 0(除了函数),而 null 转为二进制是一整串 0

2.2 instanceof

  1. 只能判断引用类型,无法判断原始类型
  2. 它是通过隐式原型链来查找 x 是否隶属于 X 这个类型

同样用代码展示

let a = 'hello'
let num = 123
let f = true 
let d = undefined
let n = null   // 返回 object
let sy = Symbol(1)
let big = 1223445326n
let arr = []// 返回 object
let obj = {} // 返回 object
let  fn = function(){}
console.log(a instanceof String);//false
console.log(num instanceof Number);//false
console.log(f instanceof Boolean);//false
console.log(d instanceof undefined);//报错
console.log(n instanceof null);//报错
console.log(sy instanceof Symbol);//false
console.log(big instanceof BigInt);//false
console.log(arr instanceof Array);//true
console.log(obj instanceof Object);//true
console.log(fn instanceof Function);//true

运行结果:

image.png

可以看到,nullundefined由于不存在,所以判断时会报错,且只能判断引用类型,那他的原型链查找底层原理是什么呢?让我们用代码一一拆解

function myInstanceof(left, right){
    if(typeof left !== 'object' && typeof left !== 'function' || left == null ){
        return false
    }
    while(left !== null){
        if(left.__proto__ === right.prototype){
            return true
        }
        left = left.__proto__
    }
    return false
}
console.log(myInstanceof(function(){},Function));

1.如果左边待判断的变量不是对象、不是函数或者左边是 null /undefined,直接返回 false,这就是说string/number/boolean 等原始类型直接返回false

2.判断当前对象的隐式原型 __proto__是否 等于 右边构造函数的显式原型 prototype,相等 → 就是它的实例 → return true

3.不相等就继续往上找,直到到找到原型链终点 null 仍未匹配,return false

3.3 Object.prototype.toString.call(x)

我们先看执行效果再讨论原理,代码示例:

let s = 'hello'
let num = 123
let f = true
let u = undefined
let n = null
let sy = Symbol(1)
let big = 12343242n
let arr = []
let obj = {}
let fn = function(){}

console.log(Object.prototype.toString.call(s));
console.log(Object.prototype.toString.call(num));
console.log(Object.prototype.toString.call(arr).slice(8, -1));
console.log(Object.prototype.toString.call(fn).slice(8, -1)); // '[object Function]'

image.png

该方法无论原始类型或引用类型都能判断,输出为[object 对象类型]。而要让其输出类型字符串,则要补充:字符串slice方法:

slice方法可以实现将字符串从左端下标切割到右端下标,左闭右开,文中slice(8, -1)指切到最左端但不包含

看到了这么强大的方法,我们不禁会想其原理,而要了解其原理,就得先;了解Object.prototype.toString(x)的原理,首先我们抛砖引玉给出官方文档:

image.png 相信你看完肯定一脸懵逼,让我来给你详细拆解:

  1. 如果 this 值为 undefined,则返回 "[object Undefined]"。

  2. 如果此值为 null,则返回 "[object Null]"。

  3. 令 O 为调用 ToObject 并将 this 值作为参数传递所得到的结果。 // const O = ToObject(this)
    // O 永远都是 Object

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

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

这下你懂了Object.prototype.toString(x)原理是ToObject创建一个对象o并将 this 值作为参数传递,而由隐式绑定规则,该对象中this永远指向Object的原型函数,即Object,js又默认执行const class = O.[[calss]],class即为Object,加上显示绑定call后,相当于执行:

Object.prototype.toString = function() {
//   const O = ToObject(this)    // this == new Number(123)
//   const class = O.[[calss]]   // { [[calss]]: Number }
//   return "[object " + calss + "]"
// }

强制改变this指向创建的实例对象,从而实现类型准确判断。

补充;Array.isArray()

Array.isArray()是专门用于判断数组的方法,挂载方式为Array.isArray = function(){},不是在Array原型函数上,因此const arr = [] arr.isArray // 错,这样的判断是错误的。

总结

四种类型判断方式能力对比如下:

方法原始类型引用类型null数组
typeof
instanceof
Object.prototype.toString
Array.isArray仅数组

记住一句话:

typeof 看身份证

instanceof 查族谱

Array.isArray 查户口本

Object.prototype.toString 看出生证明

当你不知道该用什么的时候:

Object.prototype.toString 永远是最稳妥的选择。