类数组对象是什么,如何转化为数组

一、什么是类数组对象

类数组对象(Array-like Object)是一种特殊的对象,具有以下特征:

  1. length 属性 - 表示元素个数
  2. 按索引存储数据 - 键名为数字(0, 1, 2...)
  3. 不具备数组方法 - 没有 push()pop()forEach() 等方法
js 复制代码
// 典型的类数组对象
const arrayLike = {
  0: 'a',
  1: 'b',
  2: 'c',
  length: 3
};

// 但不是数组
console.log(Array.isArray(arrayLike)); // false
console.log(typeof arrayLike); // "object"
console.log(arrayLike.length); // 3
console.log(arrayLike[0]); // "a"
// console.log(arrayLike.push('d')); // 报错:arrayLike.push is not a function

二、常见的类数组对象

1. 函数的 arguments 对象

js 复制代码
function example() {
  console.log(arguments); // Arguments(3) [1, 2, 3]
  console.log(Array.isArray(arguments)); // false
  console.log(arguments.length); // 3
  console.log(arguments[0]); // 1
}

example(1, 2, 3);

2. DOM 元素集合

js 复制代码
// NodeList
const nodeList = document.querySelectorAll('div');
console.log(nodeList); // NodeList(3) [div, div, div]
console.log(Array.isArray(nodeList)); // false

// HTMLCollection
const htmlCollection = document.getElementsByTagName('div');
console.log(htmlCollection); // HTMLCollection(3)

3. 字符串

js 复制代码
const str = 'hello';
console.log(str.length); // 5
console.log(str[0]); // 'h'
console.log(Array.isArray(str)); // false

4. TypedArray 相关对象

js 复制代码
const buffer = new ArrayBuffer(16);
const int32View = new Int32Array(buffer);
// Int32Array 是真正的数组,但有些视图对象是类数组

三、类数组对象转数组的10种方法

方法1:Array.from() (ES6+,推荐)

js 复制代码
const arrayLike = {0: 'a', 1: 'b', 2: 'c', length: 3};

const arr1 = Array.from(arrayLike);
console.log(arr1); // ['a', 'b', 'c']
console.log(Array.isArray(arr1)); // true

// 可配合映射函数
const arr2 = Array.from(arrayLike, x => x.toUpperCase());
console.log(arr2); // ['A', 'B', 'C']

方法2:扩展运算符(...) (ES6+)

js 复制代码
// 适用于可迭代的类数组对象
function example() {
  const arr = [...arguments];
  console.log(arr); // [1, 2, 3]
  console.log(Array.isArray(arr)); // true
}
example(1, 2, 3);

// NodeList
const divs = [...document.querySelectorAll('div')];
console.log(Array.isArray(divs)); // true

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

js 复制代码
const arrayLike = {0: 'a', 1: 'b', 2: 'c', length: 3};

const arr = Array.prototype.slice.call(arrayLike);
// 或简写为:
// const arr = [].slice.call(arrayLike);

console.log(arr); // ['a', 'b', 'c']
console.log(Array.isArray(arr)); // true

// arguments 示例
function sum() {
  const args = Array.prototype.slice.call(arguments);
  return args.reduce((a, b) => a + b, 0);
}
console.log(sum(1, 2, 3)); // 6

方法4:Array.prototype.concat.apply()

js 复制代码
const arrayLike = {0: 'a', 1: 'b', 2: 'c', length: 3};

const arr = Array.prototype.concat.apply([], arrayLike);
console.log(arr); // ['a', 'b', 'c']

方法5:手动遍历转换

js 复制代码
const arrayLike = {0: 'a', 1: 'b', 2: 'c', length: 3};

// 方法1:for循环
const arr1 = [];
for (let i = 0; i < arrayLike.length; i++) {
  arr1.push(arrayLike[i]);
}
console.log(arr1); // ['a', 'b', 'c']

// 方法2:Array.from的polyfill实现
function arrayFrom(arrayLike) {
  const arr = [];
  for (let i = 0; i < arrayLike.length; i++) {
    arr[i] = arrayLike[i];
  }
  return arr;
}
console.log(arrayFrom(arrayLike)); // ['a', 'b', 'c']

方法6:使用 Object.values() (仅适用于键值为数字的情况)

js 复制代码
const arrayLike = {0: 'a', 1: 'b', 2: 'c', length: 3};

const arr = Object.values(arrayLike).filter(
  (_, index) => index !== 'length' || !isNaN(index)
);
console.log(arr); // ['a', 'b', 'c', 3]
// 注意:这种方法可能包含length属性,需要过滤

方法7:使用 for...of 循环(要求对象可迭代)

js 复制代码
// 字符串转数组
const str = 'hello';
const arr = [];
for (const char of str) {
  arr.push(char);
}
console.log(arr); // ['h', 'e', 'l', 'l', 'o']

方法8:使用 Array.from 的映射能力

js 复制代码
// 将类数组对象的属性名和值都转换
const arrayLike = {0: 'a', 1: 'b', 2: 'c', length: 3};

// 转换键名和键值
const arr = Array.from({length: arrayLike.length}, (_, i) => ({
  index: i,
  value: arrayLike[i]
}));
console.log(arr);
// [
//   {index: 0, value: 'a'},
//   {index: 1, value: 'b'},
//   {index: 2, value: 'c'}
// ]

方法9:使用 Array.prototype.splice.call()

js 复制代码
const arrayLike = {0: 'a', 1: 'b', 2: 'c', length: 3};

const arr = [];
Array.prototype.splice.call(arr, 0, 0, ...Object.values(arrayLike)
  .filter((_, i) => i < arrayLike.length));
console.log(arr); // ['a', 'b', 'c']

方法10:利用 Generator 函数

js 复制代码
function* arrayLikeToArray(arrayLike) {
  for (let i = 0; i < arrayLike.length; i++) {
    yield arrayLike[i];
  }
}

const arrayLike = {0: 'a', 1: 'b', 2: 'c', length: 3};
const arr = [...arrayLikeToArray(arrayLike)];
console.log(arr); // ['a', 'b', 'c']

四、各种转换方法对比

方法 优点 缺点 适用场景
Array.from() 简洁,ES6标准,支持映射 需要ES6+环境 现代浏览器/Node.js
扩展运算符 简洁直观 要求对象可迭代 可迭代的类数组对象
slice.call() 兼容性好 代码较长 需要兼容旧浏览器
手动遍历 完全可控 代码冗长 需要特殊处理时
Object.values() 可获取所有值 需要过滤非数字键 简单的对象转换

五、实际应用示例

示例1:处理函数参数

js 复制代码
// 旧方法
function sumOld() {
  const args = Array.prototype.slice.call(arguments);
  return args.reduce((a, b) => a + b, 0);
}

// 新方法
function sumNew(...args) { // 直接使用剩余参数
  return args.reduce((a, b) => a + b, 0);
}

// 或
function sumFrom() {
  return Array.from(arguments).reduce((a, b) => a + b, 0);
}

console.log(sumOld(1, 2, 3, 4)); // 10
console.log(sumNew(1, 2, 3, 4)); // 10

示例2:DOM操作

js 复制代码
// 将NodeList转换为数组进行操作
const buttons = Array.from(document.querySelectorAll('button'));
// 或
// const buttons = [...document.querySelectorAll('button')];

buttons.forEach(button => {
  button.addEventListener('click', () => {
    console.log('Button clicked!');
  });
});

// 使用数组方法
const redButtons = buttons.filter(btn => btn.classList.contains('red'));
const buttonTexts = buttons.map(btn => btn.textContent);

示例3:处理字符串

js 复制代码
// 字符串转数组的多种方式
const str = 'hello';

const arr1 = Array.from(str); // ['h', 'e', 'l', 'l', 'o']
const arr2 = [...str]; // ['h', 'e', 'l', 'l', 'o']
const arr3 = str.split(''); // ['h', 'e', 'l', 'l', 'o']
const arr4 = Object.values(str); // ['h', 'e', 'l', 'l', 'o'] (但可能不是最佳选择)

// 反转字符串
const reversed = Array.from(str).reverse().join('');
console.log(reversed); // 'olleh'

六、注意事项

  1. length属性的重要性:类数组对象必须有正确的length属性
js 复制代码
// 错误的length会导致问题
const badArrayLike = {0: 'a', 1: 'b', length: 1};
console.log(Array.from(badArrayLike)); // ['a'],第二个元素被忽略
  1. 稀疏数组处理
js 复制代码
const sparseArrayLike = {0: 'a', 2: 'c', length: 3};
const arr = Array.from(sparseArrayLike);
console.log(arr); // ['a', undefined, 'c']
  1. 性能考虑:对于大数据集,不同方法性能有差异
js 复制代码
// 性能测试
const largeArrayLike = {length: 1000000};
for (let i = 0; i < 1000000; i++) {
  largeArrayLike[i] = i;
}

// 测试不同转换方法的性能
console.time('Array.from');
const arr1 = Array.from(largeArrayLike);
console.timeEnd('Array.from');

console.time('slice.call');
const arr2 = Array.prototype.slice.call(largeArrayLike);
console.timeEnd('slice.call');

七、总结建议

  1. 现代开发 :优先使用 Array.from() 或扩展运算符
  2. 兼容性要求 :使用 Array.prototype.slice.call()
  3. NodeList/HTMLCollection :使用 Array.from() 或扩展运算符
  4. arguments对象 :建议使用ES6的剩余参数语法 (...args)
  5. 字符串转数组Array.from(str)[...str](支持Unicode字符)

选择方法时考虑:

  • 代码可读性
  • 浏览器兼容性
  • 性能需求
  • 是否需要映射/转换功能
相关推荐
代码猎人3 分钟前
substring和substr有什么区别
前端
pimkle3 分钟前
visactor vTable 在移动端支持 ellipsis 气泡
前端
donecoding3 分钟前
告别 scrollIntoView 的“越级滚动”:一行代码解决横向滚动问题
前端·javascript
0__O3 分钟前
如何在 monaco 中实现自定义语言的高亮
前端·javascript·编程语言
Jasmine_llq5 分钟前
《P3200 [HNOI2009] 有趣的数列》
java·前端·算法·线性筛法(欧拉筛)·快速幂算法(二进制幂)·勒让德定理(质因子次数统计)·组合数的质因子分解取模法
呆头鸭L6 分钟前
快速上手Electron
前端·javascript·electron
Aliex_git11 分钟前
性能指标笔记
前端·笔记·性能优化
秋天的一阵风11 分钟前
🌟 藏在 Vue3 源码里的 “二进制艺术”:位运算如何让代码又快又省内存?
前端·vue.js·面试
松涛和鸣11 分钟前
48、MQTT 3.1.1
linux·前端·网络·数据库·tcp/ip·html
helloworld也报错?12 分钟前
保存网页为PDF
前端·javascript·pdf