普通视图

发现新文章,点击刷新页面。
今天 — 2025年5月17日首页

JavaScript中的类型判断方法你知道几种?

作者 哆啦美玲
2025年5月17日 08:09

hello~好久不见,这里是哆啦美玲哈哈!最近觉得记住知识点最好的方法,还是回顾的时候写文章分享给大家最有用,虽然我写的一般,大家将就看吧。今天就是分享一个js知识点——如何判断数据类型?

image.png

数据类型

js中的数据类型分两种:基本类型和引用类型对象。

基本类型(原始类型) 包括:number、string、boolean、null、undefined、symbol、bigInt

引用类型包括 :Object、array、function、Data

类型判断的方法

1. typeof 判断原始类型

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

那typeof是怎么使用的呢?我们直接看下面的代码:

// 判断基本类型
console.log(typeof 'hello'); //string
console.log(typeof 123); // number
console.log(typeof true); // boolean
console.log(typeof null); // object
console.log(typeof undefined); // undefined
console.log(typeof Symbol(1)); // symbol
console.log(typeof 123n); // bigint

// 判断引用类型
console.log(typeof {}); // object
console.log(typeof []); // object
console.log(typeof new Date()); // object
console.log(typeof function foo(){}); // function

由此,我们可以发现,直接使用 typeof 关键词去判断基本类型时,只有null一种类型会被误判成为object,其他基本数据类型都能够成功判断。

其实在之前的文章中解释过: 了解JavaScript的底层——内存机制 - 掘金

这里我们再解释一次,因为JS在判断类型时,会把变量的值转换成二进制进行判断;在计算机中所有的引用类型的二进制前三位是0,而null转换成二进制全部都是0,所以被误判成是对象。此外,最特殊的函数使用typeof是可以准确的判断出是function。

2. instanceof 判断引用类型
2.1 原理

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

我们直接看代码举例:

console.log({} instanceof Object);  // instanceof关键字(隶属于) 输出true
console.log([] instanceof Array);  // true
console.log(new Date() instanceof Date);  // true
console.log(function(){} instanceof Function);  // true

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

// console.log(null instanceof null); // 报错,右边必须是对象 
console.log("hello" instanceof String);  // false
console.log(123 instanceof Number);  // false
console.log(true instanceof Boolean);  // false

从上面的代码结果来看,数组、函数等对象,不仅能够判断它们是否隶属于他们本身的构造函数,还能判断是否是一个对象。所以instanceof关键字是判断隶属于的关系,即判断某个变量是否隶属于某种类型,返回true或者false。而在第10行中我们如果执行它,会得到报错提示:Right-hand side of 'instanceof' is not an object,这告诉我们instanceof的右边必须放一个对象才能进行判断。

但是它到底是怎么判断的呢?我们看下面一段代码:

function Car(){
    this.run = 'running'
}

Bus.prototype = new Car()

function Bus(){
    this.name = 'BYD'
}

let bus = new Bus();

console.log(bus instanceof Bus); // true  bus.__proto__ == Bus.prototype
console.log(bus instanceof Car); // true  bus.__proto__.__proto__ == Car.prototype
console.log(bus instanceof Object); // true bus.__proto__.__proto__.__proto__ == Object.prototype

我们自己创造一个构造函数Bus,并且基于Bus构建一个实例对象bus,所以13行代码返回true很好理解。然后我们又创造了一个Car的构造函数,并且让Bus的对象原型是Car的实例对象后,我们打印14行的结果是true,为什么呢?

其实,根据代码的结果,我们应该可以猜到instanceof可能是根据原型链来进行判断,我们直接来看bus、Bus和Car之间有什么关联:

首先我们回顾new的原理:将构造函数的显示原型(Object.prototype) 赋值给 实例对象的对象原型(即隐式原型obj.__ proto__)。(不懂的可以去看这篇文章:搞懂this,如此简单 - 掘金

所以11行代码构造实例对象bus时,会实现 bus.__ proto__ == Bus.prototype。

而第五行代码Bus.prototype = new Car(),其中Bus.prototype也是一个对象,它也存在对象原型(隐式原型),所以我们人为的将构造函数Car的显示原型赋值给它的对象原型,即 Bus.prototype.__ proto__ == Car.prototype。所以我们就可以将Bus.prototype替换掉,得到 Bus.prototype._ proto_ == bus.__ proto__._ proto_ == Car.prototype

第15行代码也同理,顺着原型链进行追溯,相信你们也可以推理得到 bus.__ proto__.__ proto__.__ proto__ == Object.prototype。所以,instanceof就是根据原型链来判断数据是否为引用类型。

2.2 手写myInstanceof方法

前面我们已经搞清楚了instanceof的判断原理,所以我们也可以手戳一个instanceof方法,实现同理。这里我直接展示我使用的两种方法:循环和递归,代码如下:

// 方法一:while循环
function my_instanceof(L, R) {
    while (L !== null) {
        L = L.__proto__
        if (L === R.prototype) {
            return true
        }
    }
    return false
}
console.log(my_instanceof([], Object));

// 方法二:递归
function myInstanceof(L, R) {
    if (L !== null) {
        L = L.__proto__
        if (L !== R.prototype) {
            return myInstanceof(L, R); 
            //每次递归的判断结果 (true 或 false) 都需要返回给上一层应该是直接返回递归的结果,
            // 确保每层递归都能够返回正确的判断。
        }
        return true
    }
    return false

}
console.log(myInstanceof([],Array)); 
2.3 包装类
console.log(new String('hello') instanceof String); // true
console.log("hello" instanceof String); // false

我们总说let str = 'hello' 和 let str = new String('hello') 是一样的,但是为什么上面代码会出现两个结果呢?这就是我们要聊的包装类的问题。

在JavaScript中,并没有像Java中那样明确的“包装类”概念。JavaScript是一种动态类型语言,变量的类型是可以在运行时决定的,因此并不需要使用包装类来将原始数据类型(基本类型)封装成对象。然而,JavaScript仍然有一些类似的概念,尤其是与基本数据类型(例如 string、number、boolean)相关的对象封装

  1. 显式创建包装对象,即直接用对应的构造函数创建变量,会将原始数据类型包装为对象,并允许访问它们的属性和方法。例如:构建字符串对象 let str = new String('abc')。
  2. 基本数据类型在V8执行下会自动被封装为对象类型。当我们访问字符串、数字、布尔值等原始类型的属性或方法时,JavaScript会自动将它们包装成相应的包装类对象。即在V8的眼里let a = 1会被执行成 let a = new Number(1)
  3. 原始类型不能拥有属性和方法,属性和方法只能是引用类型的。
  4. 访问对象身上不存在的属性不会报错,会是undefined

方便理解,我举个例子:

let num = 123  // let num = new Number(123)
num.a = 1  // {a:1}
// delete num.a
console.log(num.a); // undefined
console.log(num); // 123

代码执行的逻辑如下:

  1. 第一行代码在V8眼里会执行成let num = new Number(123),所以第二行代码能够往num上添加a属性。
  2. 我们需要的num是原始类型,不是包装类,V8严格按照原始类型不能拥有属性和方法这一原理,会将num身上的a属性移除,即delete num.a
  3. 访问Number对象 num身上不存在的属性是不会报错的,会返回undefined。
  4. 读取值的时候会执行:读取[[PrimitiveValue]]的值。如下图,num内部其实有一个内置属性[[PrimitiveValue]],是只有V8能够访问的用来存取原始类型的值的属性。

f52ac2729f2399424b0e7aac7774797.png

上面例子是没有直接显示创建包装类的情况,如果是显示创建的情况如下: image.png

我们会发现,显示创建包装类的情况下,V8是直接当做一个对象进行处理的,所以可以往其身上添加属性和方法。

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

Object.prototype.toString().call(x)是借助Object原型上的toString方法在执行过程中会读取 X 的内部属性[[Class]]这一机制来进行数据的类型判断。如图: f14dd0e23455a33df72e10eb9abc0a2.png

在官方文档中关于Object.prototype.toString()触发时会执行以下步骤描述如下::

  1. 如果 this 值为 undefined,返回 “[object Undefined]”。
  2. 如果 this 值为 null,返回 “[object Null]”。
  3. 设 O 为调用 ToObject(C打造的V8用的方法) 的结果,并将 this 值作为参数,即V8会执行ToObject(this)
  4. class 为 O 的 [[Class]] 内部属性的值,即class等于O的数据类型。
  5. 返回 String 值,该值是将三个字符串拼接—— "[object " 、 class 和 "]"

根据上面的步骤,我总结为:Object.prototype.toString()会读取this的值身上的内置属性[[Class]] ——对象的类型,以一个很特殊的状态返回 “[object ”、class 和 “]”

所以这个方法关键在于 this 指向谁,这就要使用call() 将this显示绑定在需要判断的对象上,就能够返回能够显示对象类型的字符串。(有不熟悉call的可以看这篇:搞懂this,如此简单 - 掘金

测试代码如下:

let a = 1 
let b = {}
console.log(Object.prototype.toString.call(a)); // [object Number] 
console.log(Object.prototype.toString.call(b)); // [object Object]
4. Array.isArray() 判断数组

Array.isArray()是专门用来判断一个对象是不是数组的方法,代码如下:

let arr = []
// 判断一个对象是不是数组
console.log(Array.isArray({}));

好了,我的分享到这里就结束啦~喜欢我的分享记得给我点个赞喔!

ღ( ´・ᴗ・ `)比心,我们下次再见!!

image.png

❌
❌