JS 类型判断不用愁:4 种方法,覆盖所有场景

0 阅读6分钟

前言

在JavaScript世界里,“类型判断”就像给变量“验明正身”——只有摸清每个变量的“身份”,才能避免代码里的“乌龙事件”。今天就结合实操代码,把typeof、instanceof、Object.prototype.toString.call()、Array.isArray()这几个方法讲明白,新手也能轻松拿捏~

一、先搞懂:JS的两种数据类型

在开始判断之前,我们先明确JS的基础分类,这是判断类型的前提👇

1. 原始类型(基本类型)—— 不可再分的“最小单元”

原始类型是JS中最基础的数据类型:

// 原始类型示例(你的代码片段)
let n = 123;        // number(数字类型)
let s = 'hello';    // string(字符串类型)
let f = true;       // boolean(布尔类型)
let u = undefined;  // undefined(未定义类型)
let nu = null;      // null(空值类型)
let sy = Symbol(1); // Symbol(唯一标识类型)
let big = 123123123n; // BigInt(大整数类型,解决普通数字精度问题)

2. 引用类型(复杂类型)—— 由原始类型组合而成

引用类型是“容器”,里面可以装原始类型或其他引用类型:

// 引用类型示例(你的代码片段)
let arr = [];               // Array(数组)
let obj = {};               // Object(普通对象)
let fn = function() {};     // Function(函数)
let date = new Date();      // Date(日期对象)

⚠️ 关键区别:引用类型存的是“地址”(指针),而原始类型存的是“具体值”——这也是后续类型判断出现差异的核心原因!

二、常用类型判断方法详解

下面结合代码,逐一拆解4种常用判断方法,优缺点、使用场景一次性说清,附代码验证和补充解释~

1. typeof —— 最常用,但有“小盲区”

typeof是最基础的判断方法,用法超简单:typeof 变量(或typeof(变量))👇

typeof:判断除null外的所有原始类型
console.log(typeof n);    // number
console.log(typeof(s));   // string
console.log(typeof f);    // boolean
console.log(typeof u);    // undefined
console.log(typeof sy);   // symbol
console.log(typeof big);  // bigint

typeof,原始类型里除了null,它都能精准判断

❌ typeof的“盲区”:判断引用类型+null的坑
console.log(typeof(nu));  // object(坑!明明是null)
console.log(typeof(arr)); // object(坑!明明是数组)
console.log(typeof(obj))  // object(正确,但不够精准)
console.log(typeof(date)) // object(坑!明明是日期)
console.log(typeof(fn))   // function(唯一能正确判断的引用类型)

为什么会这样?

typeof是通过“二进制编码”判断类型的:二进制前三位是0的,会统一识别为object。

所有引用类型(数组、对象、日期等)的二进制前三位都是0,所以typeof判断它们都返回object;而null的二进制全是0,也被误判为object,这是JS的历史遗留bug,至今没修复~

2. instanceof —— 引用类型“专属判断器”

typeof搞不定引用类型,instanceof就登场了!它的用法是:变量 instanceof 构造函数,核心是“通过隐式原型链查找”判断归属👇

function myinstanceof(L, R) {
  // 先排除原始类型(除了null,null在这里会被过滤)
  if (typeof(L) !== 'object' && typeof(L) !== 'function' || L == null) {
    return false
  }
  // 沿着隐式原型链(__proto__)一直往上找
  while (L.__proto__ !== null) { 
    // 如果找到构造函数的原型(R.prototype),就返回true
    if (L.__proto__ === R.prototype) {
      return true
    }
    L = L.__proto__ // 没找到,继续往上找
  }
  return false // 找到最顶层(Object.prototype.__proto__ = null),仍没找到,返回false
}

console.log(myinstanceof([], Array));  // true(数组是Array构造的)
console.log(myinstanceof([], Object)); // true(数组的原型链最终指向Object)
console.log(myinstanceof(null, Object));// false(null不是对象,被提前过滤)
❌ instanceof的“短板”:搞不定原始类型

比如let n = 123(number类型),用instanceof判断会返回false,因为原始类型没有原型链,无法通过原型查找判断归属:

console.log(123 instanceof Number); // false(原始类型number,不是Number对象)
console.log(new Number(123) instanceof Number); // true(这是Number对象,属于引用类型)

3. Object.prototype.toString.call(x) —— 全能判断王

如果说typeof有盲区,instanceof有短板,那这个方法就是“全能选手”,能精准判断所有类型👇

核心原理

Object.prototype.toString.call(x)的执行逻辑,你注释里写的很完整,我们补充得更易懂一点:

  1. 调用Object.prototype上的toString方法(注意:不是变量自身的toString,比如数组的toString会返回字符串,而这个是Object的原生方法);

  2. 将括号里的x(也就是要判断的变量),作为toString方法里的this值;

  3. 通过ToObject方法,将this值转为对象(如果是原始类型,会自动包装成对应对象,比如123会变成Number对象);

  4. 获取这个对象的[[class]]内部属性(这个属性是JS内部的,无法直接访问,它的值就是创建这个对象的构造函数名称);

  5. 返回格式为“[object 构造函数名称]”的字符串,比如判断字符串会返回“[object String]”。

✅ 实操演示
function getType(x) {
  const val = Object.prototype.toString.call(x); // 得到"[object 类型名]"
  const valType = val.slice(8, -1); // 截取字符串,去掉"[object "和"]",得到纯类型名
  return valType;
}

// 测试所有类型,精准无错!
console.log(getType(n));    // Number(注意:首字母大写,和typeof的返回值区分)
console.log(getType(s));    // String
console.log(getType(f));    // Boolean
console.log(getType(u));    // Undefined
console.log(getType(nu));   // Null(终于能正确判断null了!)
console.log(getType(sy));   // Symbol
console.log(getType(big));  // BigInt
console.log(getType(arr));  // Array
console.log(getType(obj));  // Object
console.log(getType(fn));   // Function
console.log(getType(date)); // Date

这个方法是唯一能精准判断null的方法,而且不管是原始类型还是引用类型,都能返回最准确的类型名,日常开发里用它准没错~

4. Array.isArray(x) —— 数组“专属判断”

虽然Object.prototype.toString.call(x)能判断数组,但针对数组,还有一个更简洁的方法:Array.isArray(x),原理和你写的x.__proto__ === Array.prototype一致,都是判断变量的原型是否是Array的原型。

console.log(Array.isArray(arr)); // true
console.log(Array.isArray([]));  // true
console.log(Array.isArray({}));  // false

小技巧:如果只是判断是否是数组,用Array.isArray(x)比toString方法更简洁,性能也更好~

三、总结:不同场景该用哪个方法?

怕记混?一张表格搞定,直接对号入座~

判断场景推荐方法备注
快速判断原始类型(除null)typeof简单高效,新手首选
判断引用类型(数组、函数、日期等)instanceof / Object.prototype.toString.call(x)instanceof适合判断“是否属于某个构造函数”,toString适合精准判断类型
判断nullObject.prototype.toString.call(x) / x === nulltypeof会误判,必须用这两种方法
判断数组Array.isArray(x)最简洁、最高效
需要精准判断所有类型(通用场景)Object.prototype.toString.call(x)全能选手,无盲区

看到这里,相信你已经搞懂JS类型判断啦!其实核心就是分清“原始类型”和“引用类型”,再根据场景选对方法,以后写代码,再也不用为“变量是什么类型”发愁啦!