引言 —— 为什么 类型判断 是前端面试的常青题?
很多初学者把类型判断当成八股文,但真实场景里它无处不在:接口返回的数据到底是数组还是类数组?第三方 SDK 传进来的参数是函数还是对象?低代码平台里,用户输入的 1 是数字还是字符串...... 一旦判断失误,轻则功能异常,重则线上白屏。本文将从底层原理到实战应用,系统剖析 JavaScript 类型判断的逻辑与技巧。
一、原始类型和引用类型
| 分类 | 成员(7+6) | 特点 |
|---|---|---|
| 原始类型 | number、string、boolean、undefined、null、symbol、bigint | 存储在栈中,按值访问,无隐式原型 |
| 引用类型 | Object、Array、Function、Date、Set、Map 等 | 存储在堆中,按引用访问,有隐式原型 |
注意:原始类型本身没有隐式原型 __proto__,但 V8 引擎在访问属性时会临时创建包装类对象。
示例代码
const str = 'hello'
str.toUpperCase() // 内部:new String(str).toUpperCase()
str.x = 1 // 内部:new String(str).x = 1
console.log(str.x) // undefined,包装类立即被销毁
二、typeof
typeof 是 JavaScript 中最早支持的类型判断运算符,其核心原理是通过将值转换成二进制来判断类型,直接读取值的二进制类型标识。它能准确判断基本类型(number, string, boolean, undefined, symbol),以及函数类型(返回 function),但存在两个明显缺陷。
2.1 对 null 的误判
console.log(typeof null); // object
这一问题源于 JavaScript 语言设计初期的错误。V8 引擎通过 Tagged Pointer 技术存储值的类型标记,null 被编码为 0x0,其类型标记与对象共用一个 bit 位,导致 typeof 误判。
2.2 无法判断引用类型
对于对象、数组、正则表达式等引用类型,除了 function 以外,typeof 统一返回 object 。
console.log(typeof {}); // object
console.log(typeof []); // object
console.log(typeof new Date());// object
三、instanceof
instanceof 能准确判断引用类型,其判断机制是通过对象的隐式原型链来查找是否存在某一项等于右边的 prototype。由于 v8 有包装类的过程,不能判断原始类型。其语法为:
object instanceof Constructor
object 为某个实例对象,Constructor 为某个构造函数(JS 内置的构造函数有Array、Date、Error、Function、Object、RegExp等)。检测 object 是否为 Constructor 的实例对象。
核心原理
- 获取 Constructor.prototype
- 沿着 object. ___ proto___ 向上遍历原型链
- 若找到与 Constructor.prototype 匹配的对象,返回 true
function myInstanceOf(left, right) {
if(!left.__proto__) return false;
while(left){
if(left.__proto__ === right.prototype) return true;
left = left.__proto__;
}
}
使用场景
[] instanceof Array // true
[] instanceof Object // true
new Date() instanceof Date // true
function Foo(){}; new Foo() instanceof Foo // true
四、Object.prototype.toString.call()
该方法通过调用 Object.prototype.toString,访问对象的内部 [[Class]] 属性。加上 call ,改变 toString 的 this 指向,可以将 Object 上的 toString 方法引用在其他类型身上。其核心代码:
function toType(obj) {
return Object.prototype.toString.call(obj)
.slice(8, -1);
}
核心原理
- 隐式将参数转为对象(基本类型自动包装)
- 通过内部方法 OrdinaryToPrimitive 获取内部类型
- 返回格式为 [object Type] 的字符串
关键优势
- 能准确区分所有 JavaScript 内置类型,返回值为字符串
- 避免 typeof 对 null 和 array 的错误判断
- 比 instanceof 更可靠,不受跨框架影响
使用示例
toType(null) // "Null"
toType(undefined) // "Undefined"
toType(new Set()) // "Set"
toType(/abc/) // "RegExp"
toType(Symbol()) // "Symbol"
五、实践建议
| 目标类型 | 推荐方法 | 示例代码 |
|---|---|---|
| 原始类型 | typeof | typeof x === 'number' |
| 数组 | Array.isArray | Array.isArray(x) |
| 自定义类实例 | instanceof | x instanceof MyClass |
| 通用判断 | Object.prototype.toString | getType(x) |
| 判断空对象 | 组合判断 | getType(x) === 'object' && Object.keys(x).length === 0 |