从数组到对象:JavaScript 遍历语法全解析(ES5 到 ES6 + 超详细指南)

一、为什么需要遍历语法?从数据处理的核心需求说起

在 JavaScript 开发中处理集合数据是高频需求。例如:

  • 数组求和:[1,2,3].reduce((a,c)=>a+c, 0)
  • 对象属性遍历:for (const key in user) console.log(key)
  • DOM 批量操作:document.querySelectorAll('button').forEach(btn => btn.disabled = true)

核心问题:如何高效安全地访问集合中的每个元素?本文系统解析各类遍历语法,覆盖数组、对象、类数组、Map/Set 等全场景,并深入探讨异步遍历、性能优化、内存管理等进阶内容。

二、数组遍历:从基础到函数式编程

1. 基础遍历:for 循环与 for...of

(1)for 循环:手动控制索引

ini 复制代码
const numbers = [10, 20, 30];
let sum = 0;
for (let i = 0; i < numbers.length; i++) {
  sum += numbers[i]; // 性能最优 适合大数据量
}

适用场景:需要中途 break/continue 或精确控制循环次数。

(2)for...of:简洁的迭代语法

dart 复制代码
for (const num of numbers) {
  console.log(num); // 直接获取元素值 支持所有可迭代对象
}

扩展:结合 entries () 获取索引:

javascript 复制代码
for (const [index, num] of numbers.entries()) {
  console.log(`索引${index}: ${num}`);
}

2. 函数式遍历:forEach/map/filter/reduce

(1)forEach:纯数据处理

javascript 复制代码
numbers.forEach((num, index) => {
  console.log(`第${index+1}个元素:${num}`); // 无需手动管理索引
});

注意:无法使用 break 中断 可通过 return 跳过当前迭代。

(2)map/filter:数据转换与筛选

ini 复制代码
const doubled = numbers.map(n => n * 2); // [20, 40, 60]
const filtered = numbers.filter(n => n > 20); // [30]

链式调用:numbers.filter(n => n>10).map(n=>n*2)。

(3)reduce:累加器模式

javascript 复制代码
const total = numbers.reduce((acc, cur) => acc + cur, 0); // 60

高级用法:对象合并 数组去重等。

3. 高级方法:some/every/find/flatMap

(1)条件判断:some/every

ini 复制代码
const hasBigNumber = numbers.some(n => n > 50); // false
const allSmall = numbers.every(n => n < 100); // true

(2)元素查找:find/findIndex

ini 复制代码
const users = [{ id: 1 }, { id: 2 }];
const target = users.find(u => u.id === 2); // { id: 2 }

(3)扁平化映射:flatMap

perl 复制代码
const words = ["a b c".split(' '), "d e f".split(' ')].flatMap(arr => arr); // ["a","b","c","d","e","f"]

三、对象遍历:键值对的安全访问

1. 传统遍历:for...in 与原型链陷阱

javascript 复制代码
const user = { name: 'Alice', age: 28 };
for (const key in user) {
  if (user.hasOwnProperty(key)) { // 必须过滤原型链属性
    console.log(`${key}: ${user[key]}`);
  }
}

缺陷:遍历顺序不确定 包含原型链可枚举属性。

2. ES5 + 方法:keys/values/entries

javascript 复制代码
// 获取属性名数组
const keys = Object.keys(user); // ["name", "age"]
// 遍历键值对(推荐)
for (const [key, value] of Object.entries(user)) {
  console.log(`${key}: ${value}`);
}

3. Symbol 键处理:getOwnPropertySymbols/Reflect.ownKeys

ini 复制代码
const obj = { [Symbol('id')]: 123 };
const symbolKeys = Object.getOwnPropertySymbols(obj); // [Symbol(id)]
const allKeys = Reflect.ownKeys(obj); // [Symbol(id)](包含所有类型键)

四、类数组与特殊数据结构遍历

1. 类数组(NodeList/arguments)

(1)转换为数组:Array.from/ 展开运算符

ini 复制代码
const nodeList = document.querySelectorAll('div');
const divArray = Array.from(nodeList); // 转换后使用数组方法
divArray.forEach(div => div.classList.add('active'));

(2)强制调用数组方法:call/apply

ini 复制代码
Array.prototype.forEach.call(nodeList, div => {
  div.style.color = 'red'; // 直接操作类数组
});

2. Map/Set 遍历

(1)Map:键值对遍历

arduino 复制代码
const map = new Map([['a', 1], ['b', 2]]);
for (const [key, value] of map) {
  console.log(key, value); // 'a' 1 'b' 2
}

(2)Set:值遍历(自动去重)

arduino 复制代码
const set = new Set([1, 2, 1]);
for (const item of set) {
  console.log(item); // 1 2
}

3. TypedArray 与 Generator

(1)TypedArray:二进制数据遍历

arduino 复制代码
const buffer = new ArrayBuffer(4);
const arr = new Uint8Array(buffer);
arr[0] = 65; // ASCII 'A'
for (const byte of arr) {
  console.log(byte); // 65
}

(2)Generator:可中断迭代

javascript 复制代码
function* gen() {
  yield 1;
  yield 2;
}
const it = gen();
console.log(it.next().value); // 1(手动控制迭代)

五、异步遍历:处理 Promise 与流数据

1. for...of 结合 async/await

ini 复制代码
const fetchUrls = async () => {
  const urls = ['https://api1', 'https://api2'];
  for (const url of urls) {
    const data = await fetch(url).then(res => res.json()); // 串行请求
  }
};

2. 异步迭代器:for await...of

javascript 复制代码
async function* asyncGen() {
  await new Promise(resolve => setTimeout(resolve, 1000));
  yield 'a';
}
(async () => {
  for await (const item of asyncGen()) {
    console.log(item); // 1秒后输出'a'
  }
})();

六、性能对比与最佳实践

1. 基准测试(100 万次遍历)

语法 耗时 场景推荐
for 80ms 高性能计算(如游戏循环)
forEach 220ms 业务逻辑处理
for...of 280ms 通用迭代(兼容可迭代对象)

2. 内存管理陷阱

(1)闭包引用泄漏

javascript 复制代码
// 错误:闭包持有DOM引用
const btns = document.querySelectorAll('button');
btns.forEach((btn, i) => {
  btn.onclick = () => console.log(i); // btn引用未释放
});

解决方案:使用弱引用或及时解除绑定。

(2)异步循环中的变量作用域

javascript 复制代码
// 正确:let块级作用域
for (let i = 0; i < 5; i++) {
  setTimeout(() => console.log(i), 100); // 输出0-4
}

七、遍历语法对比表格(终极版)

场景 推荐语法 核心特性 兼容性
数组索引遍历 for 手动控制 性能最优 全兼容
数组函数式遍历 forEach/map 语法简洁 支持链式调用 ES5+
对象安全遍历 Object.entries() 避免原型链污染 ES6+
类数组操作 Array.from() 转换为数组后使用数组方法 ES6+
异步数据迭代 for await...of 处理 Promise 流数据 ES2021+
Symbol 键获取 Object.getOwnPropertySymbols() 专门获取 Symbol 类型键 ES6+
二进制数据遍历 TypedArray + for...of 高效处理 ArrayBuffer 数据 ES6+
可中断迭代 Generator + yield 支持中途暂停和恢复 ES6+

八、总结:选择遍历语法的黄金法则

  1. 数组优先原则

    简单场景用 for...of 性能优先用 for 函数式需求用 map/filter。

  2. 对象遍历安全

    字符串键用 Object.entries () Symbol 键用 Reflect.ownKeys ()。

  3. 类数组转换

    始终先转为数组(Array.from) 避免直接操作类数组。

  4. 异步场景

    串行用 for...of + await 并行用 Promise.all () 流数据用异步迭代器。

  5. 内存与性能

    大数据量用 for 循环 避免 for...in 遍历数组 及时释放闭包引用。

通过掌握不同语法的适用边界和性能特性 你可以在开发中精准选择工具 写出既高效又易维护的代码。遇到复杂场景时 结合 MDN 文档和实际测试 总能找到最佳解决方案!

相关推荐
90后小陈老师32 分钟前
3D个人简历网站 5.天空、鸟、飞机
前端·javascript·3d
chenbin___32 分钟前
react native text 显示 三行 超出部分 中间使用省略号
javascript·react native·react.js
Hello World......35 分钟前
互联网大厂Java面试:从Spring到微服务的全面探讨
java·spring boot·spring cloud·微服务·面试·技术栈·互联网大厂
漫路在线4 小时前
JS逆向-某易云音乐下载器
开发语言·javascript·爬虫·python
不爱吃糖的程序媛4 小时前
浅谈前端架构设计与工程化
前端·前端架构设计
BillKu5 小时前
Vue3 Element Plus 对话框加载实现
javascript·vue.js·elementui
郝YH是人间理想6 小时前
系统架构设计师案例分析题——web篇
前端·软件工程
Evaporator Core6 小时前
深入探索:Core Web Vitals 进阶优化与新兴指标
前端·windows
初遇你时动了情6 小时前
html js 原生实现web组件、web公共组件、template模版插槽
前端·javascript·html
QQ2740287567 小时前
Soundness Gitpod 部署教程
linux·运维·服务器·前端·chrome·web3