参透JavaScript —— 判断数据类型的四种方式

前言

JavaScript 作为一门动态语言,其灵活性是把双刃剑。一方面带来了开发的便利性,另一方面也给我们在类型判断时带来了挑战,特别是在处理类型转换和隐式转换的时候

所以本篇文章我们将探讨 JavaScript 中的数据类型判断方式及在实际项目中的应用

数据类型回顾

JavaScript 的数据类型大致分为两类:原始类型和复杂类型

原始类型:

  • undefined:未定义
  • number:数字
  • boolean:布尔值
  • string:字符串
  • null:空值
  • symbol:符号(ES6 新增)
  • bigint:大整数(ES2020 新增)

复杂类型:

  • Object:对象
  • Array:数组
  • Function:函数
  • Date:日期
  • RegExp:正则表达式

类型判断方法详解

typeof 操作符

typeof 是最基础且常用的类型判断方法,使用简单,参阅 MDN 文档

语法:typeof 后面跟一个值或表达式,返回一个字符串,表示操作数的类型

可能返回的数据类型为:undefined、boolean、number、string、object、function、symbol、bigint

js 复制代码
// 原始类型判断
typeof "hello"; // "string"
typeof 123; // "number"
typeof true; // "boolean"
typeof undefined; // "undefined"
typeof Symbol(); // "symbol"
typeof 123n; // "bigint"

// 引用类型判断的局限性
typeof []; // "object"
typeof {}; // "object"
typeof null; // "object" (这是一个历史遗留的bug)
typeof new Date(); // "object"
typeof /regex/; // "object"

typeof function () {}; // "function"

注意,typeof 操作符一般用于判断原始类型,对于引用类型的判断存在局限性:

  1. 对引用值的判断有误,一般会返回固定的字符串 object
  2. null 会被错误的判断为 object 类型

instanceof 操作符

instanceof 用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上,返回一个布尔值,参阅 MDN 文档

也就是判断一个实例对象是否属于某个构造函数

语法为

js 复制代码
object instanceof constructor;

// object:实例对象
// constructor:构造函数

比如下面的例子

js 复制代码
const arr = [1, 2, 3];

console.log(arr instanceof Array); // true
console.log(arr instanceof Object); // true
console.log(arr instanceof Function); // false

一个构造函数的示例:

js 复制代码
function Person(name, age) {
  this.name = name;
  this.age = age;
}

const person = new Person("张三", 20);

console.log(person instanceof Person); // true

在这个例子中,实例对象 person 会通过 __proto__ 属性向上查找原型链,直到找到 Person.prototype 为止,如果找到则返回 true,如果找到顶部 null 都没有找到,则返回 false

实例对象的 __proto__ 属性指向构造函数的 prototype 对象

注意,instanceof 操作符只能用于判断引用类型,不能用于判断原始类型

还存在一个隐患是:如果其原型链被更改,可能导致判断不准确

Object.prototype.constructor

除了 nullundefined 以外,所有引用类型都有 constructor 属性,用于返回对象的构造函数,参阅 MDN 文档

我们继续以 instanceof 章节的构造函数示例来看:

所以,更新的图示如下:

实例对象的 __proto__ 属性指向构造函数的 prototype 对象

在图示中,person__proto__ 会等于 Person.prototype,所以 person 实例对象上也有一个 constructor 属性

而且,无论一个变量是通过构造函数方式还是字面量方式,都会有一个 constructor 属性

字面量方式在通过引擎解析时,JavaScript 后台会创建一个相应的原始包装类型对象,暴露出操作原始值的各种方法

js 复制代码
const name = "十五";
console.log(name.constructor === String); // true

const age = 18;
console.log(age.constructor === Number); // true

const arr = [1, 2, 3];
console.log(arr.constructor === Array); // true

基本上 constructor 属性能够判断出除了 nullundefined 外的所有类型

Object.prototype.toString()

一个通用且准确的类型判断方法

Object.prototype 表示所有对象的原型对象,这个对象上拥有一个 toString()方法,用于返回对象的字符串表示形式

然后搭配 call() 方法,用于改变方法内部的 this 指向

call():函数实例拥有的一个方法,传入的第一个参数可以改变函数内部的 this 指向

基本用法:

js 复制代码
const toString = Object.prototype.toString;

const name = "十五";
const age = 18;

console.log(toString.call(name)); // [object String]
console.log(toString.call(age)); // [object Number]

针对性的类型判断方法

Array.isArray()

Array.isArray() 方法用于判断传入的值是否是一个数组

js 复制代码
const arr = [1, 2, 3];

console.log(Array.isArray(arr)); // true

Number.isNaN()

Number.isNaN() 方法用于判断传入的值是否为 NaN

NaN:Not a Number,无法表示为数字的值

js 复制代码
console.log(Number.isNaN(NaN)); // true
console.log(Number.isNaN("十五")); // false
console.log(Number.isNaN(123)); // false

注意,全局的 isNaN()Number.isNaN() 的区别在于前者会强制进行类型转换,后者则不会(全等判断)

在实践项目中的应用

封装判断类型的工具函数

在真实的项目场景中,我们往往可以统一封装一些工具函数,用于判断类型

比如在 utils 文件夹下新建一个 judge.js 文件,用于编写一些判断类型的工具函数

这里默认你使用了 TypeScript

ts 复制代码
/**
 * 判断是否为字符串
 * @param value 需要判断的值
 * @returns 布尔值 | true 表示是字符串,false 表示不是字符串
 */
export const isString = (value: unknown): value is string => {
  return typeof value === "string";
};

/**
 * 通用且精确的类型判断
 * @param value 需要判断的值
 * @param type 类型
 * @returns 布尔值 | true 表示符合类型,false 表示不符合
 */
const isType = (value: unknown, type: string) => {
  return (
    Object.prototype.toString.call(value).slice(8, -1).toLowerCase() === type
  );
};

借助第三方库

Lodash 是一个一致性、模块化、高性能的 JavaScript 实用工具库

它提供了许多有用的工具函数,包括类型判断函数,如 isStringisNumberisArray

总结

上面我们详细介绍了 JavaScript 中的数据类型判断方法,包括 typeofinstanceofconstructorObject.prototype.toString(),你需要注意的是:

  1. 选择合适的判断方法
  • 判断原始类型时,优先使用 typeof
  • 判断继承关系时,使用 instanceof
  • 需要通用、准确的判断方式时,使用 Object.prototype.toString.call()
  1. 实际项目场景下的应用
  • 考虑封装工具函数
  • 借助成熟的第三方库,如 Lodash

参透JavaScript系列

本文已收录至《参透JavaScript系列》,全文地址:我的 GitHub 博客 | 掘金专栏

对你有帮助的话,欢迎 Star

交流讨论

对文章内容有任何疑问、建议、或发现有错误,欢迎交流和指正

相关推荐
Larcher2 分钟前
新手也能学会,100行代码玩AI LOGO
前端·llm·html
徐子颐14 分钟前
从 Vibe Coding 到 Agent Coding:Cursor 2.0 开启下一代 AI 开发范式
前端
小月鸭27 分钟前
如何理解HTML语义化
前端·html
jump6801 小时前
url输入到网页展示会发生什么?
前端
诸葛韩信1 小时前
我们需要了解的Web Workers
前端
brzhang1 小时前
我觉得可以试试 TOON —— 一个为 LLM 而生的极致压缩数据格式
前端·后端·架构
yivifu1 小时前
JavaScript Selection API详解
java·前端·javascript
这儿有一堆花1 小时前
告别 Class 组件:拥抱 React Hooks 带来的函数式新范式
前端·javascript·react.js
十二春秋2 小时前
场景模拟:基础路由配置
前端
六月的可乐2 小时前
实战干货-Vue实现AI聊天助手全流程解析
前端·vue.js·ai编程