JS精准判断一个对象是不是数组的方法
作者:yqcoder
为什么判断数组这么难?
在 JavaScript 中,数组本质上也是一种对象。
console.log(typeof []); // "object"
console.log(typeof {}); // "object"
因为 typeof 只能区分基本类型(string, number, boolean, undefined, symbol, bigint)和引用类型(统一返回 object,函数返回 function),所以它无法直接告诉我们:“嘿,这是个数组!”
我们需要更高级的手段来揭开它的真面目。
1. 王者方案:Array.isArray()
这是 ES5 引入的标准方法,也是现代开发中的首选。
console.log(Array.isArray([])); // true
console.log(Array.isArray({})); // false
console.log(Array.isArray("hello")); // false
console.log(Array.isArray(null)); // false
优点
- 语义清晰:代码可读性极高,一眼就能看懂意图。
- 准确可靠:内部实现机制完善,能正确处理各种边界情况。
- 跨上下文安全:即使数组来自不同的 iframe 或 window,它依然能正确识别(因为它不依赖原型链继承关系,而是检查内部标签)。
缺点
- 兼容性:不支持 IE8 及以下版本(但在 2024 年,这几乎不再是问题)。
博主建议:
无脑选它! 除非你有极其特殊的兼容需求,否则 Array.isArray 就是你的唯一选择。
2. 经典方案:instanceof
instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
const arr = [];
console.log(arr instanceof Array); // true
const obj = {};
console.log(obj instanceof Array); // false
优点
- 直观:符合面向对象编程的思维逻辑,“这个实例是不是由 Array 构造函数创建的?”
缺点
跨上下文失效:这是最大的坑!
如果页面中有多个 iframe,每个 iframe 都有自己独立的执行环境(也就是独立的 Array 构造函数)。
// 假设 iframe 中的数组传到了主窗口 iframeArray instanceof Array; // false! // 因为 iframeArray 的原型链指向的是 iframe 里的 Array.prototype, // 而主窗口里的 Array 是另一个构造函数。
- 可被篡改:如果开发者手动修改了原型链,
instanceof可能会给出错误结果。
3. 通用方案:Object.prototype.toString
这是一种“黑科技”写法,利用对象内部的 [[Class]] 属性(在 ES5 之前称为 Class 内部属性,现在通过 toString 暴露)。
const arr = [];
console.log(Object.prototype.toString.call(arr)); // "[object Array]"
const obj = {};
console.log(Object.prototype.toString.call(obj)); // "[object Object]"
const str = "hello";
console.log(Object.prototype.toString.call(str)); // "[object String]"
优点
- 万能 钥匙:不仅能判断数组,还能精准判断
Date,RegExp,Function,Null,Undefined等所有内置类型。 - 跨上下文安全:和
Array.isArray一样,它不受不同 window/iframe 的影响,因为它读取的是底层引擎标记。
缺点
- 写法繁琐:
Object.prototype.toString.call(obj)太长,容易写错。 - 可读性差:新手可能看不懂这是在干嘛。
技巧:
你可以封装一个工具函数:
const typeCheck = (obj) => Object.prototype.toString.call(obj).slice(8, -1); console.log(typeCheck([])); // "Array"
4. 避坑指南:typeof与constructor
陷阱 1:typeof
如前所述,typeof [] 返回 "object",完全无法区分数组和普通对象。千万别用!
陷阱 2:constructor
const arr = []; console.log(arr.constructor === Array); // true
看起来不错?但它有两个致命弱点:
可被修改:arr.constructor 是可以被人为赋值的,不可信。
arr.constructor = String; console.log(arr.constructor === Array); // false (误判!)
null/undefined 报错:null 和 undefined 没有构造函数,访问会报错。
5. 跨框架/跨窗口场景的特殊陷阱
这是高级前端面试的常客。
场景:你的主页面嵌入了一个 iframe,iframe 里有一个数组 iframeArr,你把它传回主页面。
| 方法 | 结果 | 原因 |
|---|---|---|
iframeArr instanceof Array | ❌ False | 主页面的 Array 和 iframe 的 Array 不是同一个引用。 |
Array.isArray(iframeArr) | ✅ True | 规范规定它检查内部插槽,不依赖原型链。 |
Object.prototype.toString.call(iframeArr) | ✅ “[object Array]” | 同样基于内部标签,不受上下文影响。 |
结论:
在涉及多窗口、Web Worker 或微前端架构时,严禁使用 instanceof 判断数组,必须使用 Array.isArray 或 toString 方法。
6. 总结与最佳实践
| 方法 | 准确性 | 跨上下文安全 | 可读性 | 推荐指数 |
|---|---|---|---|---|
Array.isArray() | ⭐⭐⭐⭐⭐ | ✅ 是 | ⭐⭐⭐⭐⭐ | 🌟🌟🌟🌟🌟 |
Object.prototype.toString | ⭐⭐⭐⭐⭐ | ✅ 是 | ⭐⭐ | 🌟🌟🌟 |
instanceof | ⭐⭐⭐ | ❌ 否 | ⭐⭐⭐⭐ | 🌟🌟 |
constructor | ⭐⭐ | ❌ 否 | ⭐⭐⭐ | ❌ 不推荐 |
typeof | ❌ 无效 | - | ⭐⭐⭐⭐⭐ | ❌ 禁止使用 |
博主寄语:
判断数组看似简单,实则暗藏玄机。
记住一条铁律:
永远优先使用 Array.isArray()。
它不仅是最标准的 API,也是最安全、最易读的选择。
只有在需要同时判断多种类型(如写一个通用的 typeOf 工具库)时,才考虑使用 Object.prototype.toString。
以上就是JS精准判断一个对象是不是数组的方法的详细内容,更多关于JS判断对象是不是数组的资料请关注脚本之家其它相关文章!
