js区分数组和对象

84 阅读2分钟

一、基础判断方法(适用 90% 场景)

1. Array.isArray()
  • 优点:ES5+ 标准方法,直接判断是否为数组。
  • 局限性:无法区分普通对象与其他类型对象(如 DateRegExp)。
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

扩展场景:区分其他内置对象(如 DateMap):

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 仅能区分基本类型(如 numberstring),对于引用类型统一返回 "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 通过重写 pushsplice 等变异方法实现响应式,但直接修改索引(如 arr[0] = val)不会触发更新;
  • 对象:Vue 通过 Object.defineProperty 劫持属性的 getter/setter,但新增属性(如 obj.newKey = val)需用 Vue.set 手动触发响应式。

六、性能对比与最佳实践

方法执行速度兼容性推荐场景
Array.isArray()最快ES5+常规数组判断
Object.prototype.toString.call()较快全兼容需严格区分所有对象类型
instanceof中等全兼容非跨窗口环境

七、总结

  1. 优先用 Array.isArray() 判断数组;
  2. Object.prototype.toString.call() 区分所有内置对象;
  3. 避免仅依赖 lengthconstructor 判断;