🔍 JS 类型检测双雄:typeof vs instanceof 深度解析
"这个变量是数组吗?"
"这个对象是哪个类的实例?"
面对这些问题,你该选谁?
typeof:轻量级、快速,适合基本类型。instanceof:重量级、精准,适合引用类型和继承关系。
但它们都有各自的"盲区"。今天,我们就来一次彻底的拆解。
📂 目录
- [⚡ 一句话总结核心区别](#⚡ 一句话总结核心区别)
- [🧐
typeof:基本类型的守护者](#🧐 typeof:基本类型的守护者) - [🔗
instanceof:原型链的侦探](#🔗 instanceof:原型链的侦探) - [⚠️ 经典陷阱与局限性](#⚠️ 经典陷阱与局限性)
- [🏆 终极方案:如何完美判断所有类型?](#🏆 终极方案:如何完美判断所有类型?)
- [💡 总结](#💡 总结)
1. ⚡ 一句话总结核心区别
| 特性 | typeof |
instanceof |
|---|---|---|
| 主要用途 | 判断基本数据类型 | 判断引用数据类型 及继承关系 |
| 返回值 | 字符串(如 "number", "object") |
布尔值(true / false) |
| 检测原理 | 检查变量存储值的类型标签 | 检查构造函数的 prototype 是否在对象的原型链上 |
对 null 的表现 |
"object" (历史 Bug) |
false |
对 Array 的表现 |
"object" (无法区分) |
true (能准确识别) |
口诀 :
基本类型用
typeof,引用类型
instanceof。数组空值有坑点,
万能方法在后头。
2. 🧐 typeof:基本类型的守护者
typeof 是一个操作符(不是函数),它返回一个表示类型的字符串。
✅ 正确用法
javascript
typeof 100; // "number"
typeof "Hello"; // "string"
typeof true; // "boolean"
typeof undefined; // "undefined"
typeof Symbol(); // "symbol"
typeof 10n; // "bigint"
typeof function () {}; // "function"
❌ 两大陷阱
陷阱 1:null 被识别为 "object"
javascript
typeof null; // "object" 😱
原因 :这是 JS 早期的设计缺陷(Bug)。在底层二进制表示中,null 的前三位是 000,与对象相同。为了兼容性,这个 Bug 一直保留至今。
陷阱 2:所有引用类型都返回 "object"
javascript
typeof []; // "object"
typeof {}; // "object"
typeof new Date(); // "object"
typeof /regex/; // "object"
后果 :你无法通过 typeof 区分数组、普通对象、日期或正则表达式。
3. 🔗 instanceof:原型链的侦探
instanceof 用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
语法 :object instanceof Constructor
✅ 正确用法
javascript
[] instanceof Array; // true
{} instanceof Object; // true
new Date() instanceof Date; // true
// 继承关系检测
function Parent() {}
function Child() {}
Child.prototype = new Parent();
const c = new Child();
c instanceof Child; // true
c instanceof Parent; // true (因为原型链上有 Parent)
c instanceof Object; // true (所有对象最终都指向 Object)
🧠 底层原理
instanceof 的判断逻辑大致如下:
javascript
function myInstanceof(left, right) {
// 获取右操作数(构造函数)的原型
let prototype = right.prototype;
// 获取左操作数(实例)的原型
left = left.__proto__; // 或者 Object.getPrototypeOf(left)
while (true) {
if (left === null || left === undefined) return false;
if (left === prototype) return true;
// 沿着原型链向上查找
left = left.__proto__;
}
}
关键点 :它检测的是原型链,而不是构造函数本身。这意味着它可以检测继承关系。
4. ⚠️ 经典陷阱与局限性
❌ 陷阱 1:基本类型无法使用 instanceof
javascript
"hello" instanceof String; // false 😱
123 instanceof Number; // false 😱
true instanceof Boolean; // false 😱
原因:
"hello"是基本类型字符串,没有原型链。String是包装对象。- 只有
new String("hello")这样的对象才会返回true。
注意 :虽然
typeof "hello"是"string",但"hello" instanceof String是false。这是一个常见的面试坑。
❌ 陷阱 2:跨窗口/iframe 失效
在不同的全局执行上下文(如 iframe)中,每个窗口都有自己独立的 Array、Object 等构造函数。
javascript
// 假设 iframe 中的数组
const iframeArray = iframeWindow.array;
iframeArray instanceof Array; // false!
// 因为 iframeWindow.Array !== window.Array
原因:它们的原型对象不同,尽管功能一样,但引用地址不同。
❌ 陷阱 3:手动修改原型链会导致误判
javascript
function Foo() {}
function Bar() {}
Bar.prototype = new Foo(); // 修改原型链
const b = new Bar();
b instanceof Foo; // true (即使 b 不是直接由 Foo 创建的)
这虽然是特性,但在复杂继承结构中可能导致混淆。
5. 🏆 终极方案:如何完美判断所有类型?
既然 typeof 和 instanceof 都有缺陷,有没有一种万能方法?
答案是:Object.prototype.toString.call()
💻 为什么它是王者?
每个对象内部都有一个 [[Class]] 属性(在 ES5 之前不可访问,但现在通过 toString 暴露)。Object.prototype.toString 会读取这个内部属性,返回标准格式 [object Type]。
✅ 实战代码
javascript
const typeCheck = (val) => {
return Object.prototype.toString.call(val);
};
// 基本类型
typeCheck(100); // "[object Number]"
typeCheck("Hello"); // "[object String]"
typeCheck(true); // "[object Boolean]"
typeCheck(undefined); // "[object Undefined]"
typeCheck(null); // "[object Null]" ✅ 完美解决 null 问题
typeCheck(Symbol()); // "[object Symbol]"
// 引用类型
typeCheck([]); // "[object Array]" ✅ 完美区分数组
typeCheck({}); // "[object Object]"
typeCheck(new Date()); // "[object Date]"
typeCheck(/regex/); // "[object RegExp]"
typeCheck(function () {}); // "[object Function]"
🛠️ 封装一个实用工具函数
javascript
function getType(val) {
// 截取 "[object Type]" 中的 "Type" 并转小写
return Object.prototype.toString.call(val).slice(8, -1).toLowerCase();
}
console.log(getType([])); // "array"
console.log(getType(null)); // "null"
console.log(getType(new Date())); // "date"
优势:
- 准确:能区分所有内置类型。
- 兼容 :不受跨窗口影响(因为调用的是原始
Object.prototype上的方法)。- 统一:返回格式一致,易于处理。
💡 总结
| 场景 | 推荐方法 | 示例 |
|---|---|---|
| 判断基本类型 | typeof |
typeof x === 'number' |
| 判断函数 | typeof |
typeof x === 'function' |
| 判断数组/日期/正则 | Array.isArray() / instanceof / toString |
Array.isArray(x) (最推荐数组) |
| 判断 null | 严格相等 | x === null |
| 判断通用对象类型 | Object.prototype.toString |
getType(x) === 'object' |
| 判断继承关系 | instanceof |
x instanceof Parent |
🚀 博主寄语 :
不要迷信某一种方法。
- 简单场景 用
typeof。- 数组判断 首选
Array.isArray()。- 复杂类型或需要精确分类 时,请毫不犹豫地使用
Object.prototype.toString.call()。记住口诀 :
typeof 辨基本,
instanceof 查原型。
空值数组有特例,
toString 称大王。
希望这篇文档能帮你彻底理清类型检测的思路!如果有疑问,欢迎在评论区留言。👇
喜欢这篇文章吗?记得点赞、收藏、转发哦! ❤️