前言:在 JavaScript 中,类型判断是编程中的一个常见需求。由于 JavaScript 是一种动态类型语言,变量的类型可以在运行时改变,因此准确判断变量的类型显得尤为重要。今天让我们一起来把类型判断掌握透彻。
一、 数据类型的分类
我们想要判断一个变量是什么类型,是不是得要先知道有哪些类型啊。我们先来了解下JavaScript里有哪些数据类型
JavaScript的数据类型分为两大类
1.基本数据类型
- string
- number
- boolean
- undefined
- null
- symbol
- bigint
我们通常会把这些基本数据类型称为原始类型
2.引用数据类型
object(包括普通对象、数组、函数、正则表达式等)
所以说 JavaScript编程语言有八种数据类型。
二、类型判断的常用方法
1. typeof 运算符
typeof 可以准确判断除了null之外的所有原始类型, 不能判断引用类型 (除了function)
console.log(typeof 'hello');
console.log(typeof 123);
console.log(typeof true);
console.log(typeof undefined);
console.log(typeof Symbol(1));
console.log(typeof 111n);
console.log(typeof null);
运行结果
我们可以看到除了null,其他的数据类型都判断对了。那为什么判断null的时候会输出一个object呢?其实,typeof null 返回 "object" 是 JavaScript 中的一个历史遗留问题,可以追溯到 JavaScript 最初的实现阶段。
在JavaScript中,所有值在底层都用二进制表示。
对于对象类型值,JavaScript 使用了一种叫做 标签表示法(tagged representation) 的方式,在二进制表示中使用前三位存储值的类型标签。如果值是对象类型,底层二进制代码表示的前三位是000,null被转换为二进制时候是一整串0没有1,所以会被错误地解析为对象类型。
我们接下来看看typeof判断引用类型会发生什么
console.log(typeof {});
console.log(typeof []);
console.log(typeof new Date());
console.log(typeof function () { });
运行结果
我们发现除了function都输出了object,typeof不是不能用来判断引用类型吗?为什么现在可以判断function?
其实是因为在 JavaScript 早期的设计中,typeof 被用作轻量级的类型检测工具。函数是 JavaScript 的核心特性之一,因此设计者选择为函数赋予了独立的类型标识。
既然typeof不能判断引用类型(除了function),那有没有什么方法可以判断引用类型呢?其实是有的,我们继续往后看。
2. instanceof 运算符
instanceof 用于检测某个对象是否是某个构造函数的实例。
示例
console.log({} instanceof Object);// {} 是否隶属于 Object
console.log([] instanceof Array);
console.log(new Date() instanceof Date);
console.log(function () { } instanceof Function);
运行结果
instanceof运算符的判断逻辑是判断一个对象是否是某个构造函数的实例,如果是,则返回true。
instanceof 通过原型链来判断类型相等,只能判断引用类型(原始类型没有隐式原型)
instanceof 的核心逻辑是通过原型链检查对象是否是某个构造函数的实例。instanceof 会检查 object 的原型链,验证是否存在Constructor.prototype。如果 object 的原型链上能找到 Constructor.prototype,则返回 true,否则返回 false。
示例
function Car() {
this.run = 'running'
}
Bus.prototype = new Car();
function Bus() {
this.name = 'BYD'
}
let bus = new Bus();
console.log(bus instanceof Car);
console.log(bus instanceof Object);
运行结果
原型链结构图为
bus -> Bus.prototype -> Car.prototype -> Object.prototype -> null
我们前面提到了instanceof不能判断基本数据类型(如 number, string, boolean 等),因为它们是值而非对象,因此,使用instanceof判断基本数据类型时,会返回false
示例
console.log(new String('hello') instanceof String);
console.log('hello' instanceof String);
运行结果
我们在上面的代码分别用构造函数和字面量创建了一个字符串,然后判断它们是否为String类型,我们之前提到过使用构造函数和直接创建一个字面量是一样的效果。但为什么判断的结果却迥然不同呢?
这是因为在执行第一行代码的时候是真的创建了一个对象,执行第二行代码的时候只是创建了一个字面量。只有当这个字面量调用方法或者访问属性时,JavaScript引擎会临时创建一个包装类对象,让你能够调用这些方法和属性。这个过程被称为装箱(boxing),操作完成后,这些临时对象会被销毁。
我们来简单介绍下什么是包装类
包装类
在 JavaScript 中,包装类(Wrapper Objects) 是指为基本数据类型(如 string、number 和 boolean)提供的对象封装形式。虽然 JavaScript 的基本数据类型是原始值(primitive types),但在某些情况下,它们会被自动包装为对象,以便调用方法或属性。
3.Object.prototype.toString()
我们前面提到的两种方法均不能同时判断基本数据类型和引用数据类型,JavaScript提供了一种更精确的类型判断方法,通过调用 Object.prototype.toString.call(value) 来判断变量的类型。
示例
console.log(Object.prototype.toString.call(null)); // "[object Null]"
console.log(Object.prototype.toString.call([])); // "[object Array]"
console.log(Object.prototype.toString.call({})); // "[object Object]"
console.log(Object.prototype.toString.call(() => {})); // "[object Function]"
console.log(Object.prototype.toString.call(/regex/)); // "[object RegExp]"
console.log(Object.prototype.toString.call(new Date())); // "[object Date]"
console.log(Object.prototype.toString.call(new Set())); // "[object Set]"
console.log(Object.prototype.toString.call(42)); // "[object Number]"
console.log(Object.prototype.toString.call("Hello")); // "[object String]"
工作原理
当你调用 Object.prototype.toString() 时,JavaScript 会检查该值的内部 [[Class]] 属性,并以 "[object Type]" 的格式返回它的类型。
-
Object.prototype.toString()方法属于Object的原型。 -
当直接调用时,它会返回调用对象的内部标签。
-
对于基本类型或内置对象,它能够精确区分。
4.Array.isArray()
我们都是数组时JavaScript内置的最强大的数据结构,它自带了一个方法来判断一个变量是不是数组
示例
console.log(Array.isArray([])); // true
console.log(Array.isArray({})); // false
console.log(Array.isArray("string")); // false
5. === 判断 null 和 undefined
null 和 undefined 是两个特殊的基本类型,可以直接使用严格相等运算符 === 判断。
示例
let a = null;
let b;
console.log(a === null); // true
console.log(b === undefined); // true
三、 不同场景下的推荐方法
- 判断基础数据类型------使用
typeof - 判断是否为数组------使用
Array.isArray() - 判断复杂对象关系------使用
Object.prototype.toString.call() - 判断构造函数实例关系------使用
instanceof - 判断
null或undefined------使用严格相等运算符===