您现在的位置是:网站首页 > 类数组对象文章详情

类数组对象

在 JavaScript 中,类数组对象是一种常见的结构,它看起来像数组,但并非真正的数组。这类对象通常具有数字索引和 length 属性,但缺乏数组的原生方法。理解类数组对象的特性和转换方式,能更灵活地处理数据。

什么是类数组对象

类数组对象是具有以下特征的对象:

  1. 使用数字作为属性名(如 0, 1, 2...)
  2. 拥有 length 属性
  3. 不具备数组方法(如 push, pop, slice 等)

典型的例子包括:

  • 函数内的 arguments 对象
  • DOM 操作返回的 NodeList(如 document.querySelectorAll 的结果)
  • 字符串(每个字符可通过索引访问)
// 典型的类数组对象
const arrayLike = {
  0: 'a',
  1: 'b',
  2: 'c',
  length: 3
};

类数组对象的检测方法

判断一个对象是否为类数组对象可以通过以下方式:

function isArrayLike(obj) {
  // 排除 null/undefined 和非对象类型
  if (obj == null || typeof obj !== 'object') return false;
  
  // 检查 length 属性
  const length = obj.length;
  return typeof length === 'number' && 
         length >= 0 && 
         length % 1 === 0 && 
         length <= Number.MAX_SAFE_INTEGER;
}

console.log(isArrayLike([1,2,3])); // true
console.log(isArrayLike({0:'a', length:1})); // true
console.log(isArrayLike('abc')); // false(字符串是原始类型)

类数组对象转为真实数组

由于类数组对象无法直接使用数组方法,通常需要先进行转换:

方法1:Array.from()

ES6 引入的最简单方式:

const arrayLike = {0: 'a', 1: 'b', length: 2};
const realArray = Array.from(arrayLike);
console.log(realArray); // ['a', 'b']

方法2:扩展运算符

适用于可迭代对象(如 argumentsNodeList):

function example() {
  return [...arguments];
}
console.log(example(1,2,3)); // [1, 2, 3]

方法3:Array.prototype.slice.call()

传统浏览器兼容方案:

const nodeList = document.querySelectorAll('div');
const nodeArray = Array.prototype.slice.call(nodeList);

方法4:手动遍历

通用性最强的方案:

function convertArrayLike(obj) {
  const arr = [];
  for (let i = 0; i < obj.length; i++) {
    arr[i] = obj[i];
  }
  return arr;
}

常见类数组对象示例

arguments 对象

函数内部自动生成的类数组对象:

function sum() {
  // 旧版转换方式
  const args = Array.prototype.slice.call(arguments);
  return args.reduce((a,b) => a + b, 0);
}

// ES6 更推荐使用剩余参数
function modernSum(...args) {
  return args.reduce((a,b) => a + b, 0);
}

DOM 集合

// NodeList
const divs = document.querySelectorAll('div');
divs.forEach(div => console.log(div)); // 现代浏览器已为 NodeList 添加 forEach

// HTMLCollection
const forms = document.forms; // HTMLCollection
Array.from(forms).forEach(form => {
  console.log(form.method);
});

字符串的类数组特性

字符串虽然原始类型,但具有类似数组的访问方式:

const str = 'hello';
console.log(str[1]); // "e"
console.log(Array.from(str)); // ["h", "e", "l", "l", "o"]

实现自定义类数组对象

通过定义索引属性和 length 属性即可创建:

const customArrayLike = {
  // 必须的属性
  0: 'first',
  1: 'second',
  length: 2,
  
  // 可选的迭代器(使对象可迭代)
  [Symbol.iterator]: function*() {
    for(let i = 0; i < this.length; i++) {
      yield this[i];
    }
  },
  
  // 自定义方法
  join: function(separator = ',') {
    return Array.from(this).join(separator);
  }
};

console.log(customArrayLike[1]); // "second"
console.log([...customArrayLike]); // ["first", "second"]
console.log(customArrayLike.join('|')); // "first|second"

类数组对象的性能考量

在某些场景下,直接操作类数组对象比转换为真实数组更高效:

// 更高效的方式(避免创建新数组)
function sumArguments() {
  let sum = 0;
  for(let i = 0; i < arguments.length; i++) {
    sum += arguments[i];
  }
  return sum;
}

// 需要多次访问时转换为数组更合适
function processElements() {
  const elements = [...arguments];
  // 多次操作...
  elements.filter(x => x > 0).map(x => x * 2);
}

与类型化数组的区别

类型化数组(如 Uint8Array)虽然也是类数组结构,但有本质不同:

特性 类数组对象 类型化数组
内存分配 普通对象 连续内存缓冲区
元素类型 任意类型 特定数值类型
性能 较低 接近原生数组
方法支持 无数组方法 有部分数组方法
// 类型化数组示例
const typedArray = new Uint8Array([1, 2, 3]);
console.log(typedArray.map(x => x * 2)); // Uint8Array [2, 4, 6]

上一篇: 稀疏数组处理

下一篇: DOM树结构

我的名片

网名:~川~

岗位:console.log 调试员

坐标:重庆市-九龙坡区

邮箱:cc@qdcc.cn

沙漏人生

站点信息

  • 建站时间:2013/03/16
  • 本站运行
  • 文章数量
  • 总访问量
微信公众号
每次关注
都是向财富自由迈进的一步