伪数组

2 阅读3分钟

一、伪数组的定义与本质

定义:伪数组(Array-like Object)是具有 length 属性索引元素,但不具备数组原生方法(如 push、forEach)的对象。
本质

  • 本质是对象,而非真正的数组实例(instanceof Array === false
  • 常见于 DOM 节点集合(如 document.querySelectorAll)、函数参数(arguments)等

二、伪数组的典型特征

  1. 必备属性

    • 具有 length 属性(数值类型,代表元素个数)
    • 元素可通过索引(0, 1, 2...)访问
  2. 缺失功能

    • 没有数组原型链上的方法(如 mapfilter
    • 调用数组方法会报错(如 obj.push(1) 会提示 push is not a function

三、常见伪数组场景

场景示例特性
函数参数arguments存储函数调用时的参数
DOM节点集合document.querySelectorAll('div')动态集合,元素增删会实时更新
字符串"abc"可通过索引访问字符,length=3
自定义对象{0: 'a', 1: 'b', length: 2}手动模拟伪数组结构

四、伪数组转真数组的方案

1. Array.from(ES6+,推荐)
const pseudoArray = {0: 1, 1: 2, 2: 3, length: 3};
const realArray = Array.from(pseudoArray); // [1, 2, 3]

// 带映射转换
const doubled = Array.from(pseudoArray, x => x * 2); // [2, 4, 6]
  • 优势:支持映射函数,语法简洁,现代浏览器兼容
2. 扩展运算符(ES6+)
const domNodes = document.querySelectorAll('div');
const nodeArray = [...domNodes]; // 转为真数组
  • 注意:需确保伪数组具有 length 属性且索引连续
3. Array.prototype.slice.call(兼容性方案)
function fn() {
  const argsArray = Array.prototype.slice.call(arguments);
  return argsArray.map(x => x * 2);
}
fn(1, 2, 3); // [2, 4, 6]
  • 原理:利用 slice 方法将伪数组转为数组
  • 兼容性:IE9+ 有效

五、问题

1. 问:伪数组和真数组的本质区别?
    • 原型链不同:真数组继承 Array.prototype,拥有数组方法;伪数组是普通对象,原型为 Object.prototype
    • 类型检测Array.isArray(真数组) 返回 true,伪数组返回 false
2. 问:为什么 DOM 节点集合是伪数组?
    • 浏览器设计时,DOM 节点集合需要动态反映文档变化(如元素删除时自动更新),而数组是静态结构;
    • 伪数组结构更轻量,无需实现数组的所有方法。
3. 问:如何判断一个对象是否为伪数组?
  • function isArrayLike(obj) {
      // 1. 非null且为对象 2. 有length属性 3. length为非负整数 4. length不超过安全整数范围
      return obj !== null && 
             typeof obj === 'object' && 
             'length' in obj && 
             Number.isInteger(obj.length) && 
             obj.length >= 0 && 
             obj.length <= Number.MAX_SAFE_INTEGER;
    }
    
4. 问:伪数组转数组的方案中,性能最好的是哪个?
    • 扩展运算符(...)Array.from 性能相近,均优于 slice.call
    • slice.call 需创建新数组并复制元素,开销略大;
    • 现代浏览器中推荐使用扩展运算符,代码更简洁。

六、伪数组的应用与最佳实践

  1. 函数参数处理

    // 不定长参数转数组
    function sum() {
      const args = Array.from(arguments);
      return args.reduce((total, num) => total + num, 0);
    }
    
  2. DOM操作优化

    // 伪数组转数组后使用数组方法
    const images = [...document.querySelectorAll('img')];
    images.filter(img => img.complete).forEach(img => console.log('已加载'));
    
  3. 自定义伪数组

    // 创建一个可遍历的伪数组
    const myArrayLike = {
      0: 'a',
      1: 'b',
      2: 'c',
      length: 3,
      forEach: Array.prototype.forEach // 手动添加数组方法
    };
    myArrayLike.forEach(item => console.log(item)); // a b c