{T}

数组迭代方法

数组迭代方法是 JavaScript 中最常用且最强大的数组操作工具,掌握它们能显著提高开发效率和代码质量。

概述

JavaScript 从 ES5 开始引入了丰富的数组迭代方法,ES6+ 进一步扩展了这些能力。这些方法提供了函数式编程风格的数据处理能力,使代码更加简洁、可读和可维护。

核心特性

  • 函数式编程:支持回调函数,代码更加声明式
  • 不可变性:大多数方法返回新数组,不改变原数组
  • 链式调用:方法可以连续调用,构建数据处理管道
  • 短路优化:部分方法支持提前终止,提升性能

一、forEach()

遍历数组的每个元素,对每个元素执行回调函数。是最基础的迭代方法。

语法

javascript
array.forEach(callback(element, index, array), thisArg);

参数详解

参数类型必填说明
callbackFunction为每个元素执行的函数
elementany-当前正在处理的元素
indexnumber-当前元素的索引(从 0 开始)
arrayArray-正在遍历的数组本身
thisArgany回调函数中 this 的值

返回值

undefined

基础示例

javascript
const arr = [1, 2, 3, 4, 5];

// 基本遍历
arr.forEach((item, index) => {
  console.log(`索引 ${index}: ${item}`);
});
// 输出: 索引 0: 1, 索引 1: 2, ...

// 使用 thisArg
const obj = { multiplier: 2 };
arr.forEach(function(item) {
  console.log(item * this.multiplier);
}, obj);
// 输出: 2, 4, 6, 8, 10

高级用法

javascript
const users = [
  { name: 'Alice', score: 85 },
  { name: 'Bob', score: 92 },
  { name: 'Charlie', score: 78 },
];

// 计算总分
let total = 0;
users.forEach((user) => {
  total += user.score;
});
console.log(`平均分: ${(total / users.length).toFixed(1)}`); // 平均分: 85.0

// 批量修改对象属性
users.forEach((user) => {
  user.grade = user.score >= 90 ? 'A' : user.score >= 80 ? 'B' : 'C';
});
// users 现在包含 grade 属性

// 使用 array 参数
arr.forEach((item, index, array) => {
  console.log(`当前元素: ${item}, 数组长度: ${array.length}`);
});

注意事项

特性说明建议
无法中断不能使用 breakcontinue需要中断时使用 for...ofsome()
不改变原数组但回调函数中可以修改元素注意引用类型的修改
稀疏数组跳过空位(empty slots)mapfilter 行为一致
无法链式调用返回 undefined需要返回值时使用 map

性能提示

javascript
// ❌ 不推荐:在 forEach 中进行大量计算
arr.forEach((item) => {
  // 复杂计算...
});

// ✅ 推荐:大数据量使用 for 循环
for (let i = 0; i < arr.length; i++) {
  // 复杂计算...
}

二、map()

创建一个新数组,其结果是对原数组每个元素调用回调函数后的返回值。是最常用的数据转换方法。

语法

javascript
const newArray = array.map(callback(element, index, array), thisArg);

返回值

新的 Array 实例,长度与原数组相同。

基础示例

javascript
const numbers = [1, 2, 3, 4, 5];

// 数值转换
const doubled = numbers.map((num) => num * 2);
console.log(doubled); // [2, 4, 6, 8, 10]

// 提取对象属性
const users = [
  { name: 'Alice', age: 25 },
  { name: 'Bob', age: 30 },
];
const names = users.map((user) => user.name);
console.log(names); // ['Alice', 'Bob']

// 格式化数据
const prices = [100, 200, 300];
const formatted = prices.map((price) => `¥${price.toFixed(2)}`);
console.log(formatted); // ['¥100.00', '¥200.00', '¥300.00']

高级用法

javascript
// 对象数组转换
const products = [
  { id: 1, name: 'Apple', price: 5 },
  { id: 2, name: 'Banana', price: 3 },
];

const formatted = products.map(({ id, name, price }) => ({
  key: id,
  label: name,
  value: price,
}));
console.log(formatted);

  // ... 中间省略 ...

// 重建对象(React 常用)
const items = ['Apple', 'Banana', 'Orange'];
const itemObjects = items.map((item, index) => ({
  id: index,
  text: item,
}));

使用场景矩阵

场景示例说明
数据转换arr.map(x => x * 2)一对一映射
属性提取users.map(u => u.name)提取单一属性
格式化nums.map(n => n.toFixed(2))统一格式
结构重组arr.map(({a,b}) => ({x:a, y:b}))改变对象结构
React 列表items.map(item => <Item key={item.id} />)JSX 渲染

性能注意事项

javascript
// ❌ 不推荐:map 中有副作用
let sum = 0;
const result = numbers.map((n) => {
  sum += n; // 副作用
  return n * 2;
});

// ✅ 推荐:map 只做转换
const result = numbers.map((n) => n * 2);
const sum = numbers.reduce((acc, n) => acc + n, 0);

// ❌ 不推荐:map 返回值不使用
numbers.map((n) => console.log(n)); // 应该用 forEach

// ✅ 推荐:使用正确的方法
numbers.forEach((n) => console.log(n));

三、filter()

过滤数组,返回满足条件的元素组成的新数组。

语法

javascript
const newArray = array.filter(callback(element, index, array), thisArg);

返回值

新的 Array 实例,仅包含使回调函数返回真值的元素。

基础示例

javascript
const numbers = [1, 2, 3, 4, 5, 6];

// 过滤偶数
const evens = numbers.filter((num) => num % 2 === 0);
console.log(evens); // [2, 4, 6]

// 过滤对象数组
const products = [
  { name: 'Apple', price: 5, inStock: true },
  { name: 'Banana', price: 3, inStock: false },
  { name: 'Orange', price: 4, inStock: true },
];
const available = products.filter((p) => p.inStock && p.price < 5);
console.log(available); // [{ name: 'Orange', price: 4, inStock: true }]

// 去除假值
const mixed = [0, 1, false, 2, '', 3, null, undefined, NaN];
const truthy = mixed.filter(Boolean);
console.log(truthy); // [1, 2, 3]

高级用法

javascript
// 多条件过滤
const data = [
  { name: 'Alice', age: 25, department: 'Engineering' },
  { name: 'Bob', age: 30, department: 'Marketing' },
  { name: 'Charlie', age: 28, department: 'Engineering' },
];

const filtered = data.filter(
  (item) => item.age >= 25 && item.age <= 28 && item.department === 'Engineering'
);
console.log(filtered); // [{ name: 'Charlie', ... }]


  // ... 中间省略 ...

  { status: 'success', data: { value: 2 } },
];
const validData = responses
  .filter((r) => r.status === 'success')
  .filter((r) => r.data !== null);
console.log(validData.length); // 2

使用技巧

javascript
// 链式过滤(更易读)
const result = data
  .filter((item) => item.active)
  .filter((item) => item.score > 60)
  .filter((item) => item.age >= 18);

// 动态条件过滤
const conditions = {
  minPrice: 10,
  maxPrice: 50,
  category: 'fruit',
};

const filtered = products.filter((p) => {
  return (
    p.price >= conditions.minPrice &&
    p.price <= conditions.maxPrice &&
    p.category === conditions.category
  );
});

// 使用解构
const filtered = users.filter(({ age, status }) => age >= 18 && status === 'active');

四、reduce()

累计器,将数组归约为单个值。是最强大、最灵活的数组方法。

语法

javascript
const result = array.reduce(callback(accumulator, currentValue, index, array), initialValue);

参数详解

参数类型说明
accumulatorany累计器,存储每次回调的返回值
currentValueany当前正在处理的元素
indexnumber当前元素的索引
arrayArray正在遍历的数组
initialValueany强烈推荐提供,作为首次回调的初始值

返回值

累计器的最终值。

基础示例

javascript
const numbers = [1, 2, 3, 4, 5];

// 求和
const sum = numbers.reduce((acc, cur) => acc + cur, 0);
console.log(sum); // 15

// 求乘积
const product = numbers.reduce((acc, cur) => acc * cur, 1);
console.log(product); // 120

// 找最大值
const max = numbers.reduce((acc, cur) => (acc > cur ? acc : cur), numbers[0]);
console.log(max); // 5

// 找最小值
const min = numbers.reduce((acc, cur) => (acc < cur ? acc : cur), numbers[0]);
console.log(min); // 1

高级用法

javascript
// 数组转对象
const users = [
  { id: 1, name: 'Alice' },
  { id: 2, name: 'Bob' },
];
const userMap = users.reduce((acc, user) => {
  acc[user.id] = user.name;
  return acc;
}, {});
console.log(userMap); // { 1: 'Alice', 2: 'Bob' }

// 统计元素出现次数

  // ... 中间省略 ...

const double = (x) => x * 2;
const addOne = (x) => x + 1;
const square = (x) => x * x;

const calculate = pipe(double, addOne, square);
console.log(calculate(3)); // ((3 * 2) + 1)² = 49

重要提示

务必提供初始值,避免意外行为:

javascript
const arr = [];

// ❌ 不提供初始值,空数组会报错
arr.reduce((acc, cur) => acc + cur); // TypeError: Reduce of empty array with no initial value

// ✅ 提供初始值则安全
arr.reduce((acc, cur) => acc + cur, 0); // 0

// ⚠️ 无初始值时的问题
const nums = [1, 2, 3];
// 无初始值:acc 第一次是 1,cur 第一次是 2
const sum1 = nums.reduce((acc, cur) => acc + cur);
// 有初始值:acc 第一次是 0,cur 第一次是 1
const sum2 = nums.reduce((acc, cur) => acc + cur, 0);
console.log(sum1 === sum2); // true(结果相同,但过程不同)

五、reduceRight()

从右向左归约数组,与 reduce() 方向相反。

语法

javascript
const result = array.reduceRight(callback(accumulator, currentValue, index, array), initialValue);

示例

javascript
const arr = [1, 2, 3, 4, 5];

// 从右向左累加
const sum = arr.reduceRight((acc, cur) => acc + cur, 0);
console.log(sum); // 15(结果相同)

// 从右向左拼接
const strs = ['a', 'b', 'c'];
const result = strs.reduceRight((acc, cur) => acc + cur, '');
console.log(result); // 'cba'

// 实现compose函数
const compose =
  (...fns) =>
  (x) =>
    fns.reduceRight((v, fn) => fn(v), x);

const double = (x) => x * 2;
const addOne = (x) => x + 1;

const calculate = compose(double, addOne);
console.log(calculate(3)); // (3 + 1) * 2 = 8

六、find() 和 findIndex()

查找满足条件的第一个元素或其索引。

find()

返回第一个满足条件的元素,否则返回 undefined

findIndex()

返回第一个满足条件的索引,否则返回 -1

语法

javascript
const element = array.find(callback(element, index, array), thisArg);
const index = array.findIndex(callback(element, index, array), thisArg);

示例

javascript
const users = [
  { id: 1, name: 'Alice', age: 25 },
  { id: 2, name: 'Bob', age: 30 },
  { id: 3, name: 'Charlie', age: 28 },
];

// find() - 查找元素
const user = users.find((u) => u.id === 2);
console.log(user); // { id: 2, name: 'Bob', age: 30 }

const notFound = users.find((u) => u.id === 99);
console.log(notFound); // undefined

// findIndex() - 查找索引
const index = users.findIndex((u) => u.name === 'Bob');
console.log(index); // 1

const notFoundIndex = users.findIndex((u) => u.id === 99);
console.log(notFoundIndex); // -1

// 查找复合条件
const result = users.find((u) => u.age > 25 && u.name.startsWith('B'));
console.log(result); // { id: 2, name: 'Bob', age: 30 }

// 查找并修改
const found = users.find((u) => u.id === 2);
if (found) {
  found.age = 31; // 注意:这会修改原数组
}

使用场景对比

场景推荐方法说明
只需元素find()直接获取对象
需要索引findIndex()用于删除、插入等操作
检查存在some()只需知道是否存在
获取所有匹配filter()获取所有符合条件的元素
javascript
// 查找后删除
const idx = users.findIndex((u) => u.id === 2);
if (idx !== -1) {
  users.splice(idx, 1);
}

// 查找后替换
const idx = users.findIndex((u) => u.id === 2);
if (idx !== -1) {
  users[idx] = { ...users[idx], name: 'Robert' };
}

七、findLast() 和 findLastIndex() (ES2023)

从数组末尾开始查找元素或索引。

语法

javascript
const element = array.findLast(callback(element, index, array), thisArg);
const index = array.findLastIndex(callback(element, index, array), thisArg);

示例

javascript
const arr = [1, 2, 3, 4, 5, 3];

// findLast() - 从末尾查找
const lastEven = arr.findLast((n) => n % 2 !== 0);
console.log(lastEven); // 3(最后一个奇数)

// findLastIndex() - 从末尾查找索引
const lastEvenIndex = arr.findLastIndex((n) => n === 3);
console.log(lastEvenIndex); // 5(最后一个3的索引)

// 对比 find 和 findLast
const firstMatch = arr.find((n) => n === 3);
const lastMatch = arr.findLast((n) => n === 3);
console.log(firstMatch, lastMatch); // 3, 3(值相同但位置不同)

const firstIndex = arr.findIndex((n) => n === 3);
const lastIndex = arr.findLastIndex((n) => n === 3);
console.log(firstIndex, lastIndex); // 2, 5

兼容性

ES2023 新增,需要现代浏览器或 Node.js 18+ 支持。


八、some() 和 every()

检测数组元素是否满足条件,返回布尔值。

some()

只要有一个元素满足条件就返回 true

every()

所有元素都满足条件才返回 true

语法

javascript
const result = array.some(callback(element, index, array), thisArg);
const result = array.every(callback(element, index, array), thisArg);

示例

javascript
const numbers = [1, 2, 3, 4, 5];

// some() - 是否存在
const hasLarge = numbers.some((num) => num > 3);
console.log(hasLarge); // true

const hasNegative = numbers.some((num) => num < 0);
console.log(hasNegative); // false

// every() - 是否全部
const allPositive = numbers.every((num) => num > 0);
console.log(allPositive); // true

  // ... 中间省略 ...


// 数据验证
const formData = { name: 'Alice', email: 'alice@example.com', age: 25 };
const requiredFields = ['name', 'email'];
const hasAllFields = requiredFields.every((field) => formData[field]);
console.log(hasAllFields); // true

短路特性

两个方法都具有短路特性,可提前终止遍历:

javascript
// some 找到第一个 true 就停止
let someCount = 0;
[1, 2, 3, 4, 5].some((num) => {
  someCount++;
  return num === 3;
});
console.log(someCount); // 3(只遍历到第三个)

// every 找到第一个 false 就停止
let everyCount = 0;
[1, 2, 3, 4, 5].every((num) => {
  everyCount++;
  return num < 3;
});
console.log(everyCount); // 3(只遍历到第三个)

空数组行为

javascript
// some 空数组返回 false(不存在满足条件的元素)
[].some((x) => true); // false

// every 空数组返回 true(所有元素都满足条件,空集的"所有"为真)
[].every((x) => false); // true ⚠️ 注意这个特殊行为

使用场景

javascript
// 权限检查
const permissions = ['read', 'write'];
const canDelete = permissions.some((p) => p === 'delete');
console.log(canDelete); // false

// 表单验证
const fields = ['email', 'password', 'confirmPassword'];
const allValid = fields.every((field) => {
  const input = document.querySelector(`#${field}`);
  return input.checkValidity();
});

// 数组比较
const arr1 = [1, 2, 3];
const arr2 = [1, 2, 3, 4];
const hasSameElements = arr1.every((n) => arr2.includes(n));

九、其他迭代方法

entries()、keys()、values()

返回数组迭代器对象,配合 for...of 使用。

语法

javascript
const iterator = array.entries(); // [index, value] 对
const iterator = array.keys();    // 索引
const iterator = array.values();  // 值

示例

javascript
const arr = ['a', 'b', 'c'];

// entries() - 返回 [index, value] 对
for (const [index, value] of arr.entries()) {
  console.log(`${index}: ${value}`);
}
// 0: a, 1: b, 2: c

// keys() - 只返回索引
for (const key of arr.keys()) {
  console.log(key);
}
// 0, 1, 2

// values() - 只返回值
for (const value of arr.values()) {
  console.log(value);
}
// a, b, c

// 转换为数组
const entries = [...arr.entries()];
console.log(entries); // [[0, 'a'], [1, 'b'], [2, 'c']]

const keys = [...arr.keys()];
console.log(keys); // [0, 1, 2]

const values = [...arr.values()];
console.log(values); // ['a', 'b', 'c']

flat() 和 flatMap()

数组扁平化(ES2019)。

语法

javascript
const newArray = array.flat(depth);     // depth 默认为 1
const newArray = array.flatMap(callback(element, index, array));

示例

javascript
// flat() - 扁平化数组
const nested = [1, [2, 3], [4, [5, 6]]];
console.log(nested.flat());   // [1, 2, 3, 4, [5, 6]](深度 1)
console.log(nested.flat(2));  // [1, 2, 3, 4, 5, 6](深度 2)
console.log(nested.flat(Infinity)); // [1, 2, 3, 4, 5, 6](完全扁平)

// 移除空位
const sparse = [1, , 3, , 5];
console.log(sparse.flat()); // [1, 3, 5]

// flatMap() - map + flat(深度 1)
const sentences = ['Hello World', 'Good Morning'];
const words = sentences.flatMap((s) => s.split(' '));
console.log(words); // ['Hello', 'World', 'Good', 'Morning']

// 复杂示例:映射并扁平化
const data = [
  { name: 'Alice', hobbies: ['reading', 'gaming'] },
  { name: 'Bob', hobbies: ['music'] },
];
const allHobbies = data.flatMap((user) => user.hobbies);
console.log(allHobbies); // ['reading', 'gaming', 'music']

// 过滤并映射
const numbers = [1, 2, 3, 4, 5];
const result = numbers.flatMap((n) => (n % 2 === 0 ? [n, n * 2] : []));
console.log(result); // [2, 4, 4, 8]

十、方法选择决策指南

决策流程图

code
需要什么结果?
├─ 无需返回值,只需遍历 → forEach()
├─ 新数组(相同长度) → map()
├─ 新数组(可能更短) → filter()
├─ 单个值(聚合) → reduce()
├─ 单个元素 → find()
├─ 索引位置 → findIndex()
├─ 布尔值(存在性) → some()
├─ 布尔值(全部性) → every()
└─ 扁平化数组 → flat() / flatMap()

方法对比表

方法返回值改变原数组可中断空数组行为适用场景
forEachundefined正常执行副作用操作
map新数组返回 []数据转换
filter新数组返回 []数据过滤
reduce任意值返回初始值聚合计算
reduceRight任意值返回初始值反向聚合
find元素或 undefined返回 undefined查找元素
findIndex索引或 -1返回 -1查找位置
findLast元素或 undefined返回 undefined反向查找
findLastIndex索引或 -1返回 -1反向查找位置
someboolean返回 false存在性检查
everyboolean返回 true完整性检查
flat新数组返回 []数组扁平化
flatMap新数组返回 []映射后扁平化

十一、性能优化建议

1. 方法选择优化

javascript
// ❌ 性能差:filter 后取第一个
const user = users.filter((u) => u.id === 1)[0];

// ✅ 性能好:使用 find
const user = users.find((u) => u.id === 1);

// ❌ 性能差:map 后 filter
const result = arr.map(transform).filter(Boolean);

// ✅ 性能好:先 filter 后 map(减少 map 操作次数)
const result = arr.filter(shouldProcess).map(transform);

2. 避免不必要的遍历

javascript
// ❌ 多次遍历
const sum = arr.reduce((a, b) => a + b, 0);
const max = Math.max(...arr);
const min = Math.min(...arr);

// ✅ 单次遍历(大数据量时)
const { sum, max, min } = arr.reduce(
  (acc, cur) => ({
    sum: acc.sum + cur,
    max: Math.max(acc.max, cur),
    min: Math.min(acc.min, cur),
  }),
  { sum: 0, max: -Infinity, min: Infinity }
);

3. 大数据量处理

javascript
// ❌ 内存占用大:链式调用创建多个中间数组
const result = largeArray
  .filter(condition1)
  .map(transform1)
  .filter(condition2)
  .map(transform2);

// ✅ 内存优化:使用 reduce 单次处理
const result = largeArray.reduce((acc, item) => {
  if (!condition1(item)) return acc;
  const transformed = transform1(item);
  if (!condition2(transformed)) return acc;
  acc.push(transform2(transformed));
  return acc;
}, []);

// ✅ 或者使用 for...of(性能最优)
const result = [];
for (const item of largeArray) {
  if (!condition1(item)) continue;
  const transformed = transform1(item);
  if (!condition2(transformed)) continue;
  result.push(transform2(transformed));
}

4. 性能对比(100万数据测试)

操作forEachmapfor 循环
简单遍历~50ms~55ms~15ms
复杂计算~200ms~210ms~180ms

💡 提示:对于简单操作,函数式方法性能足够好;对于极端性能要求,传统 for 循环更快。


十二、实战案例

案例1:数据转换管道

javascript
const products = [
  { name: 'Apple', price: 5, category: 'fruit', inStock: true },
  { name: 'Carrot', price: 2, category: 'vegetable', inStock: true },
  { name: 'Banana', price: 3, category: 'fruit', inStock: false },
  { name: 'Broccoli', price: 4, category: 'vegetable', inStock: true },
];

// 链式调用:过滤库存 -> 选择水果 -> 价格调整 -> 提取信息
const result = products
  .filter((p) => p.inStock)
  .filter((p) => p.category === 'fruit')
  .map((p) => ({
    name: p.name,
    price: p.price * 1.1, // 加价 10%
    originalPrice: p.price,
  }))
  .sort((a, b) => a.price - b.price);

console.log(result);
// [{ name: 'Apple', price: 5.5, originalPrice: 5 }]

案例2:复杂数据聚合

javascript
const orders = [
  { id: 1, product: 'Apple', quantity: 2, price: 5, date: '2024-01-01' },
  { id: 2, product: 'Banana', quantity: 3, price: 3, date: '2024-01-01' },
  { id: 3, product: 'Apple', quantity: 1, price: 5, date: '2024-01-02' },
  { id: 4, product: 'Orange', quantity: 2, price: 4, date: '2024-01-02' },
  { id: 5, product: 'Banana', quantity: 1, price: 3, date: '2024-01-03' },
];

// 按产品分组统计
const summary = orders.reduce((acc, order) => {
  const key = order.product;
  if (!acc[key]) {

  // ... 中间省略 ...

  return acc;
}, {});

// 计算总金额
const total = orders.reduce((acc, order) => acc + order.quantity * order.price, 0);
console.log(`总金额: ¥${total}`); // 总金额: ¥35

案例3:表单数据验证

javascript
const formData = {
  username: 'alice',
  email: 'alice@example.com',
  age: 25,
  password: 'abc123',
  confirmPassword: 'abc123',
};

const validationRules = {
  username: {
    validate: (v) => v.length >= 3,
    message: '用户名至少3个字符',

  // ... 中间省略 ...


// 批量验证结果
const isValid = Object.keys(validationRules).every((field) =>
  validationRules[field].validate(formData[field])
);
console.log(isValid); // true/false

案例4:数据处理与可视化

javascript
const salesData = [
  { month: 'Jan', sales: 100, cost: 80 },
  { month: 'Feb', sales: 120, cost: 90 },
  { month: 'Mar', sales: 150, cost: 100 },
  { month: 'Apr', sales: 130, cost: 95 },
  { month: 'May', sales: 180, cost: 120 },
];

// 计算利润
const withProfit = salesData.map((item) => ({
  ...item,
  profit: item.sales - item.cost,

  // ... 中间省略 ...

// 找出最佳月份
const bestMonth = salesData.reduce((best, current) =>
  current.sales > best.sales ? current : best
);

console.log(`最佳月份: ${bestMonth.month}, 销售额: ${bestMonth.sales}`);

十三、常见问题解答 (FAQ)

Q1: forEach 和 map 的区别是什么?

A: 主要区别在于返回值和使用目的:

  • forEach 返回 undefined,用于执行副作用(如打印、写入文件)
  • map 返回新数组,用于数据转换
javascript
// forEach - 执行操作
[1, 2, 3].forEach((n) => console.log(n)); // 打印,返回 undefined

// map - 转换数据
const doubled = [1, 2, 3].map((n) => n * 2); // 返回 [2, 4, 6]

Q2: reduce 的初始值什么时候可以省略?

A: 只有当数组非空且累加类型与元素类型一致时才可省略,但不推荐省略:

javascript
// 可以省略(但不推荐)
[1, 2, 3].reduce((a, b) => a + b); // 6

// 必须提供初始值
[].reduce((a, b) => a + b, 0); // 0(空数组必须提供)
[1, 2, 3].reduce((a, b) => a.concat(b), []); // 初始值类型不同

Q3: 如何跳出 forEach 循环?

A: forEach 无法真正跳出,有以下替代方案:

javascript
// 方案1:使用 some/every 的短路特性
const found = arr.some((item) => {
  if (item === target) {
    // 处理逻辑
    return true; // 中断
  }
});

// 方案2:使用 for...of
for (const item of arr) {
  if (item === target) break; // 可以中断
}

// 方案3:抛出异常(不推荐)
try {
  arr.forEach((item) => {
    if (item === target) throw new Error('found');
  });
} catch (e) {
  if (e.message !== 'found') throw e;
}

Q4: filter 和 find 的性能差异?

A: find 更快,因为找到第一个匹配就停止:

javascript
// filter - 遍历全部
const result = arr.filter(item => item.id === 1)[0];

// find - 找到即停(更快)
const result = arr.find(item => item.id === 1);

Q5: 如何实现数组的深度去重?

A: 使用 filter + JSON.stringifyreduce

javascript
// 简单值去重
const unique = [...new Set([1, 2, 2, 3])]; // [1, 2, 3]

// 对象数组去重(基于 JSON)
const arr = [{ id: 1 }, { id: 1 }, { id: 2 }];
const unique = arr.filter(
  (item, index) => arr.findIndex((i) => JSON.stringify(i) === JSON.stringify(item)) === index
);

// 对象数组去重(基于属性)
const uniqueById = arr.filter(
  (item, index, self) => self.findIndex((i) => i.id === item.id) === index
);

// 使用 reduce
const unique = arr.reduce((acc, cur) => {
  const exists = acc.some((item) => item.id === cur.id);
  return exists ? acc : [...acc, cur];
}, []);

Q6: every 对空数组返回 true 的原因?

A: 这是数学逻辑中的"空真"(vacuous truth)概念:对于空集,所有元素都满足任何条件(因为没有反例)。

javascript
[].every((x) => x > 100); // true
[].every((x) => x < 0);   // true
// 逻辑:不存在不满足条件的元素,因此"所有元素都满足"

Q7: map 和 forEach 哪个更快?

A: 性能差异很小,选择应基于语义:

  • 需要返回值 → map
  • 只需副作用 → forEach
javascript
// 性能测试(100万元素)
// map: ~55ms
// forEach: ~50ms
// 差异约 10%,可忽略

十四、浏览器兼容性

兼容性表

方法ChromeFirefoxSafariEdgeNode.js
forEach1+1.5+3+9+0.1+
map1+1.5+3+9+0.1+
filter1+1.5+3+9+0.1+
reduce1+3+4+9+0.1+
find/findIndex45+25+7.1+12+4.0+
findLast/findLastIndex97+104+15.4+97+18.0+
flat/flatMap69+62+12+79+11.0+
entries/keys/values38+28+8+12+0.12+

Polyfill 建议

javascript
// find polyfill
if (!Array.prototype.find) {
  Array.prototype.find = function (callback, thisArg) {
    for (let i = 0; i < this.length; i++) {
      if (callback.call(thisArg, this[i], i, this)) {
        return this[i];
      }
    }
    return undefined;
  };
}

// flat polyfill
if (!Array.prototype.flat) {
  Array.prototype.flat = function (depth = 1) {
    return this.reduce((acc, cur) => {
      if (Array.isArray(cur) && depth > 0) {
        acc.push(...cur.flat(depth - 1));
      } else {
        acc.push(cur);
      }
      return acc;
    }, []);
  };
}

十五、最佳实践总结

1. 方法选择原则

javascript
// ✅ 根据目的选择方法
const data = [1, 2, 3, 4, 5];

// 只需遍历
data.forEach((n) => console.log(n));

// 需要转换后的数组
const doubled = data.map((n) => n * 2);

// 需要筛选
const evens = data.filter((n) => n % 2 === 0);

// 需要聚合结果
const sum = data.reduce((acc, n) => acc + n, 0);

// 需要查找
const found = data.find((n) => n > 3);

// 需要判断
const hasEven = data.some((n) => n % 2 === 0);
const allPositive = data.every((n) => n > 0);

2. 避免常见陷阱

javascript
// ❌ 在迭代中修改数组长度
arr.forEach((item, index) => {
  if (condition) arr.splice(index, 1); // 危险!
});

// ✅ 使用 filter
const filtered = arr.filter((item) => !condition);

// ❌ map 不返回值
arr.map((item) => console.log(item));

// ✅ 使用 forEach
arr.forEach((item) => console.log(item));

// ❌ reduce 不提供初始值
arr.reduce((acc, cur) => acc + cur); // 空数组报错

// ✅ 始终提供初始值
arr.reduce((acc, cur) => acc + cur, 0);

3. 链式调用优化

javascript
// ❌ 创建多个中间数组
const result = arr
  .filter(condition1)
  .map(transform1)
  .filter(condition2)
  .map(transform2);

// ✅ 合并操作
const result = arr
  .filter((item) => condition1(item) && condition2(transform1(item)))
  .map((item) => transform2(transform1(item)));

// ✅ 或使用 reduce(大数据量)
const result = arr.reduce((acc, item) => {
  if (!condition1(item)) return acc;
  const transformed = transform1(item);
  if (!condition2(transformed)) return acc;
  acc.push(transform2(transformed));
  return acc;
}, []);

小结

数组迭代方法是 JavaScript 开发中最核心的工具之一:

方法类别方法名核心用途
遍历forEach执行副作用
转换map数据映射
过滤filter条件筛选
聚合reduce/reduceRight累计计算
查找find/findIndex/findLast/findLastIndex定位元素
判断some/every条件检测
扁平flat/flatMap数组扁平化
迭代entries/keys/values获取迭代器

Iterator Helpers(ES2025)

ES2025 为所有内置迭代器(包括数组的 entries()keys()values() 返回的迭代器)新增了辅助方法,使迭代器可以像数组一样链式操作,但不会创建中间数组,更加高效:

javascript
// Iterator Helpers 链式操作
function* naturals() {
  let i = 0
  while (true) yield i++
}

const result = naturals()
  .take(5)          // 取前 5 个: [0, 1, 2, 3, 4]
  .filter(x => x % 2 === 0)  // 过滤偶数: [0, 2, 4]
  .map(x => x * x)  // 平方: [0, 4, 16]

console.log([...result]) // [0, 4, 16]

  // ... 中间省略 ...

const pairs = [1, 2, 3].values().flatMap(x => [x, x * 2])
console.log([...pairs]) // [1, 2, 2, 4, 3, 6]

// drop() - 跳过前 N 个
const dropped = naturals().drop(3).take(3)
console.log([...dropped]) // [3, 4, 5]

Iterator Helper 方法列表:

方法说明返回类型
.map(fn)映射每个元素Iterator
.filter(fn)过滤元素Iterator
.take(n)取前 n 个元素Iterator
.drop(n)跳过前 n 个元素Iterator
.flatMap(fn)映射后扁平化Iterator
.reduce(fn, init)累计计算
.toArray()转为数组Array
.forEach(fn)遍历执行副作用undefined
.some(fn)是否存在满足条件的元素boolean
.every(fn)是否所有元素都满足条件boolean
.find(fn)查找第一个满足条件的元素

💡 核心原则:选择正确的方法,让代码更简洁、更易读、更高效。记住——函数式不是目的,清晰才是目的


扩展阅读