一、基础判断方法(适用 90% 场景)
1. Array.isArray()
- 优点:ES5+ 标准方法,直接判断是否为数组。
- 局限性:无法区分普通对象与其他类型对象(如
Date、RegExp)。
const arr = [1, 2, 3];
const obj = { a: 1 };
console.log(Array.isArray(arr)); // true
console.log(Array.isArray(obj)); // false
2. instanceof 操作符
- 原理:检查对象原型链是否包含
Array.prototype。 - 局限性:跨窗口(iframe)环境失效(不同窗口的
Array构造函数不同)。
console.log(arr instanceof Array); // true
console.log(obj instanceof Array); // false
二、高级判断方法(100% 可靠)
3. Object.prototype.toString.call()
- 原理:调用对象内部
[[Class]]属性(所有内置对象均有)。 - 返回值:
[object Type](如[object Array]、[object Object])。
const isArray = obj => Object.prototype.toString.call(obj) === '[object Array]';
const isObject = obj => Object.prototype.toString.call(obj) === '[object Object]';
console.log(isArray(arr)); // true
console.log(isObject(obj)); // true
扩展场景:区分其他内置对象(如 Date、Map):
console.log(Object.prototype.toString.call(new Date())); // [object Date]
console.log(Object.prototype.toString.call(new Map())); // [object Map]
三、基于属性的判断(非最优,但需了解)
4. 检查 length 属性
- 数组:
length是自动维护的数字属性(>=0)。 - 对象:
length可能不存在或为任意值。
const hasLength = obj => typeof obj.length === 'number';
console.log(hasLength(arr)); // true
console.log(hasLength(obj)); // false(普通对象无 length)
局限性:类数组对象(如 arguments、DOM 节点列表)也有 length,但非真正数组。
5. 检查 constructor
- 数组:
constructor指向Array。 - 对象:
constructor指向Object。
console.log(arr.constructor === Array); // true
console.log(obj.constructor === Object); // true
局限性:
- 可被修改(如
arr.constructor = null); - 自定义类实例可能误判(如
class MyArray extends Array {})。
四、安全的类型守卫(TypeScript 场景)
在 TypeScript 中,可结合类型守卫增强类型安全:
function isArray<T>(obj: unknown): obj is Array<T> {
return Array.isArray(obj);
}
function processData(data: unknown) {
if (isArray(data)) {
// TypeScript 自动推断 data 为 Array<T>
data.push(1); // 安全操作
} else if (typeof data === 'object' && data !== null) {
// 处理普通对象
}
}
五、问题
1. 问:为什么 typeof [] 返回 "object"?
答:
JavaScript 中,数组是继承自 Object 的特殊对象,typeof 仅能区分基本类型(如 number、string),对于引用类型统一返回 "object"。
2. 问:如何区分普通对象与类数组对象(如 arguments)?
答:
使用 Array.isArray() 判断是否为数组,结合 length 属性和 call/apply 方法:
function isArrayLike(obj) {
return obj &&
typeof obj === 'object' &&
typeof obj.length === 'number' &&
obj.length >= 0 &&
!Array.isArray(obj);
}
console.log(isArrayLike(arguments)); // true(在函数内部)
3. 问:在 Vue 响应式系统中,数组和对象的监听有何不同?
答:
- 数组:Vue 通过重写
push、splice等变异方法实现响应式,但直接修改索引(如arr[0] = val)不会触发更新; - 对象:Vue 通过
Object.defineProperty劫持属性的 getter/setter,但新增属性(如obj.newKey = val)需用Vue.set手动触发响应式。
六、性能对比与最佳实践
| 方法 | 执行速度 | 兼容性 | 推荐场景 |
|---|---|---|---|
Array.isArray() | 最快 | ES5+ | 常规数组判断 |
Object.prototype.toString.call() | 较快 | 全兼容 | 需严格区分所有对象类型 |
instanceof | 中等 | 全兼容 | 非跨窗口环境 |
七、总结
- 优先用
Array.isArray()判断数组;- 用
Object.prototype.toString.call()区分所有内置对象;- 避免仅依赖
length或constructor判断;