阅读视图

发现新文章,点击刷新页面。

还在无脑 .map().filter()?实测改用 Iterator Helpers 后,内存占用降低了 99%

别再把所有东西都转成数组了:Iterator Helpers 性能实测

今天看到一篇文章,标题很直接:Stop turning everything into arrays。作者的观点是,JavaScript 里我们习惯了用 .map().filter().slice() 这种链式调用,看着很优雅,但其实每一步都在创建新数组,做了很多不必要的工作。

我一开始也半信半疑,毕竟这种写法用了这么多年,真的有那么大问题吗?于是我写了个测试脚本,跑了几组对比实验,结果还挺出乎意料的。

问题在哪

先说传统数组方法的问题。假设你有个常见场景:从一个大列表里筛选出符合条件的数据,做点转换,然后只取前 10 条显示。

const visibleItems = items
  .filter(item => item.active)
  .map(item => ({ id: item.id, doubled: item.value * 2 }))
  .slice(0, 10);

这代码看着没毛病,但实际执行时:

  1. filter 遍历整个数组,创建一个新数组
  2. map 再遍历一遍,又创建一个新数组
  3. slice 最后从结果里取前 10 条,又创建一个新数组

如果 items 有 10 万条数据,你可能只需要 10 条,但前面两步已经把 10 万条全处理完了。这就是"急切求值"(eager evaluation)的问题——不管你最后用不用,先把活全干了再说。

Iterator Helpers 是什么

Iterator Helpers 是 JavaScript 新加的特性,提供了一套"惰性求值"(lazy evaluation)的方法。关键区别是:

  • 传统数组方法:每一步都立即执行,创建中间数组
  • Iterator Helpers:只描述要做什么,真正需要数据时才执行

用法上也很直观:

const visibleItems = items
  .values()              // 转成 iterator
  .filter(item => item.active)
  .map(item => ({ id: item.id, doubled: item.value * 2 }))
  .take(10)              // 只取 10 条
  .toArray();            // 最后转回数组

核心差异在于:

  1. items.values() 返回的是 iterator,不是数组
  2. 每一步只是在"描述"操作,不会立即执行
  3. take(10) 告诉它只需要 10 条,处理到 10 条就停
  4. toArray() 才真正触发执行,而且只处理需要的数据

实测对比

我写了个测试脚本,从时间空间两个维度进行对比测试。每组场景的时间测试重复 10 次取平均值,内存测试使用 Node.js 的 process.memoryUsage() API 测量堆内存增长。

场景 1:过滤 + 转换 + 取前 10 项

数据规模:100,000 条

// 传统数组方法
dataset
  .filter(item => item.active)
  .map(item => ({ id: item.id, doubled: item.value * 2 }))
  .slice(0, 10);

// Iterator Helpers
dataset
  .values()
  .filter(item => item.active)
  .map(item => ({ id: item.id, doubled: item.value * 2 }))
  .take(10)
  .toArray();

结果

维度 传统数组方法 Iterator Helpers 性能提升
时间 1.545ms 0.019ms 98.77%
内存 3.38 MB 0.01 MB 99.75%

这个结果很夸张,Iterator Helpers 在时间上快了 80 多倍,内存使用更是只有传统方法的 0.3%。原因很简单:传统方法处理了所有 10 万条数据并创建了 2 个中间数组(filter 和 map 各一个),而 Iterator Helpers 找到 10 条就停了,完全不创建中间数组。

场景 2:嵌套数据扁平化

数据规模:10,000 个父项,每个包含 10 个子项(共 100,000 条子数据)

// 传统数组方法
dataset
  .flatMap(parent => parent.children)
  .filter(child => child.value > 50)
  .slice(0, 20);

// Iterator Helpers
dataset
  .values()
  .flatMap(parent => parent.children)
  .filter(child => child.value > 50)
  .take(20)
  .toArray();

结果

维度 传统数组方法 Iterator Helpers 性能提升
时间 3.716ms 0.018ms 99.53%
内存 4.03 MB 0.01 MB 99.73%

flatMap 这种场景更明显,因为传统方法要先把所有嵌套数据(10 万条子数据)展平成一个大数组,再过滤,再切片,创建了 2 个中间数组。Iterator Helpers 直接在展平的过程中就能提前终止,内存使用几乎可以忽略不计。

场景 3:查找第一个匹配项

数据规模:1,000,000 条(目标在第 800,000 条)

// 传统数组方法
dataset.filter(item => item.id === targetId)[0];

// Iterator Helpers
dataset.values().find(item => item.id === targetId);

结果

维度 传统数组方法 Iterator Helpers 性能变化
时间 6.587ms 8.713ms -32.27% ⚠️
内存 0.01 MB 14.80 MB -194800% ⚠️

这个场景很有意思,Iterator Helpers 在时间和空间上都更差了。我分析了一下原因:

  1. 时间慢的原因:目标在数据集后半部分(第 80 万条),两者都要遍历大部分数据。filter 是原生实现,优化得很好;而 Iterator Helpers 的 find 每次迭代的开销更大
  2. 内存多的原因:这个测试结果有点反直觉,可能是因为 iterator 本身的内部状态占用了内存,而且 GC 时机不同导致测量偏差

这说明 Iterator Helpers 不是银弹,在"需要遍历大部分数据"的场景下,传统方法可能更好。

场景 4:复杂链式调用

数据规模:50,000 条

// 传统数组方法
dataset
  .filter(item => item.active)
  .map(item => ({ ...item, doubled: item.value * 2 }))
  .filter(item => item.doubled > 500)
  .map(item => ({ id: item.id, final: item.doubled + 100 }))
  .slice(0, 15);

// Iterator Helpers
dataset
  .values()
  .filter(item => item.active)
  .map(item => ({ ...item, doubled: item.value * 2 }))
  .filter(item => item.doubled > 500)
  .map(item => ({ id: item.id, final: item.doubled + 100 }))
  .take(15)
  .toArray();

结果

维度 传统数组方法 Iterator Helpers 性能提升
时间 1.421ms 0.028ms 98.02%
内存 5.33 MB 0.01 MB 99.75%

链式调用越多,传统方法创建的中间数组就越多。这个场景有 4 次操作(filter → map → filter → map),传统方法创建了 4 个中间数组,总共占用 5.33 MB 内存;而 Iterator Helpers 一个中间数组都没创建,内存使用几乎为零。

什么时候用 Iterator Helpers

根据测试结果,我总结了几个适合用 Iterator Helpers 的场景:

1. 只需要前 N 项

这是最明显的优势场景。无限滚动、分页加载、虚拟列表这些场景都适合。

// 虚拟列表只渲染可见的 20 条
const visibleRows = allRows
  .values()
  .filter(isInViewport)
  .take(20)
  .toArray();

2. 流式数据处理

处理 API 分页、SSE 流、WebSocket 消息这些场景,Iterator Helpers 配合 async iterator 很好用:

async function* fetchPages() {
  let page = 1;
  while (true) {
    const res = await fetch(`/api/items?page=${page++}`);
    if (!res.ok) return;
    yield* await res.json();
  }
}

// 只拉取需要的数据,不会一次性加载所有分页
const firstTen = await fetchPages()
  .filter(isValid)
  .take(10)
  .toArray();

3. 复杂的数据管道

如果你的数据处理链路很长,有多次 filtermapflatMap,用 Iterator Helpers 能避免创建一堆中间数组。

const result = data
  .values()
  .filter(step1)
  .map(step2)
  .flatMap(step3)
  .filter(step4)
  .take(100)
  .toArray();

什么时候不用

Iterator Helpers 也不是万能的,这几种情况还是老老实实用数组:

1. 需要随机访问

Iterator 是单向的,不能 items[5] 这样直接取某一项。如果你需要随机访问,还是得用数组。

2. 数据量很小

如果就几十条数据,用 Iterator Helpers 反而增加了复杂度,传统数组方法更简单直接。

3. 需要多次遍历

Iterator 只能遍历一次,如果你需要对同一份数据做多次不同的处理,还是先 toArray() 转成数组再说。

const iter = data.values().filter(x => x > 10);

// ❌ 第二次遍历会返回空,因为 iterator 已经消费完了
const first = iter.take(5).toArray();
const second = iter.take(5).toArray(); // []

// ✅ 先转数组,再多次使用
const filtered = data.values().filter(x => x > 10).toArray();
const first = filtered.slice(0, 5);
const second = filtered.slice(5, 10);

兼容性

Iterator Helpers 在现代浏览器和 Node.js 22+ 都已经支持了。如果你的项目还要兼容老版本,可以用 core-js 这类 polyfill。

可以在 Can I Use 查看详细的兼容性数据。

一些坑

1. Iterator 不是数组

这是最容易踩的坑。Iterator 没有 length[index] 这些属性,也不能直接 console.log 看内容。

const iter = [1, 2, 3].values();

console.log(iter.length);  // undefined
console.log(iter[0]);      // undefined
console.log(iter);         // Object [Array Iterator] {}

// 要看内容,得先转数组
console.log([...iter]);    // [1, 2, 3]

2. reduce 不是惰性的

大部分 Iterator Helpers 都是惰性的,但 reduce 是个例外,它必须遍历所有数据才能得出结果。

// reduce 会立即消费整个 iterator
const sum = data.values().reduce((acc, x) => acc + x, 0);

3. 调试不方便

因为是惰性求值,你不能在中间步骤打断点看数据。如果要调试,可以在关键步骤插入 toArray() 转成数组再看。

const result = data
  .values()
  .filter(step1)
  .toArray()  // 调试用,看看 filter 后的结果
  .values()
  .map(step2)
  .take(10)
  .toArray();

总结

Iterator Helpers 不是要替代数组,而是给了我们另一个选择。核心思路就一句话:如果你不需要整个数组,就别创建它

从实测结果看:

  • 在"取前 N 项"这类场景下,时间和空间开销都能降低 90%+
  • 在"需要遍历大部分数据"的场景下,传统方法可能更快(场景 3 是个反例)
  • 数据规模越大,Iterator Helpers 的优势越明显
  • 内存优势尤其突出:传统方法创建的中间数组会占用大量内存,而 Iterator Helpers 几乎不占用额外内存

我个人的使用建议是:

  1. 默认还是用数组方法,简单直接,不容易出错
  2. 遇到性能瓶颈时,看看是不是"只需要部分数据却处理了全部",如果是,试试 Iterator Helpers
  3. 写数据管道时,如果链路很长,用 Iterator Helpers 能让代码更清晰,也能避免不必要的内存分配
  4. 内存敏感场景,比如处理大数据集、移动端应用等,Iterator Helpers 能显著降低内存压力

完整测试代码

下面是完整的性能测试代码,包含时间空间两个维度的测试。你可以复制到本地跑一下:

/**
 * Iterator Helpers vs Array Methods 性能对比测试
 * 
 * 测试维度:
 * 1. 时间开销(执行时间)
 * 2. 空间开销(内存使用)
 * 
 * 测试场景:
 * 1. 大数据集过滤 + 转换 + 取前 N 项
 * 2. 嵌套数据扁平化
 * 3. 查找第一个匹配项
 * 4. 复杂链式调用
 */

// ============ 工具函数 ============

function generateLargeDataset(size) {
  return Array.from({ length: size }, (_, i) => ({
    id: i,
    value: Math.random() * 1000,
    category: ['A', 'B', 'C', 'D'][i % 4],
    active: i % 3 === 0
  }));
}

function benchmark(name, fn, iterations = 1) {
  const times = [];
  
  for (let i = 0; i < iterations; i++) {
    const start = performance.now();
    fn();
    const end = performance.now();
    times.push(end - start);
  }
  
  const avg = times.reduce((a, b) => a + b, 0) / times.length;
  const min = Math.min(...times);
  const max = Math.max(...times);
  
  console.log(`\n${name}:`);
  console.log(`  平均: ${avg.toFixed(3)}ms`);
  console.log(`  最小: ${min.toFixed(3)}ms`);
  console.log(`  最大: ${max.toFixed(3)}ms`);
  
  return avg;
}

function memoryBenchmark(name, fn) {
  // 强制垃圾回收(需要 Node.js 启动时加 --expose-gc 参数)
  if (global.gc) {
    global.gc();
  }
  
  const memBefore = process.memoryUsage();
  fn();
  const memAfter = process.memoryUsage();
  
  const heapUsedDiff = (memAfter.heapUsed - memBefore.heapUsed) / 1024 / 1024;
  
  console.log(`  内存增长: ${heapUsedDiff.toFixed(2)} MB`);
  
  return heapUsedDiff;
}

// ============ 测试场景 1: 过滤 + 转换 + 取前 N 项 ============

console.log('='.repeat(60));
console.log('测试场景 1: 大数据集过滤 + 转换 + 取前 10 项');
console.log('数据规模: 100,000 条');
console.log('='.repeat(60));

const dataset1 = generateLargeDataset(100000);

// 传统数组方法 - 时间
const arrayMethodTime1 = benchmark('传统数组方法 (filter + map + slice) - 时间', () => {
  const result = dataset1
    .filter(item => item.active)
    .map(item => ({ id: item.id, doubled: item.value * 2 }))
    .slice(0, 10);
  
  return result;
}, 10);

// 传统数组方法 - 内存
const arrayMethodMem1 = memoryBenchmark('传统数组方法 (filter + map + slice) - 内存', () => {
  const result = dataset1
    .filter(item => item.active)
    .map(item => ({ id: item.id, doubled: item.value * 2 }))
    .slice(0, 10);
  
  return result;
});

// Iterator Helpers - 时间
const iteratorHelpersTime1 = benchmark('Iterator Helpers (filter + map + take) - 时间', () => {
  const result = dataset1
    .values()
    .filter(item => item.active)
    .map(item => ({ id: item.id, doubled: item.value * 2 }))
    .take(10)
    .toArray();
  
  return result;
}, 10);

// Iterator Helpers - 内存
const iteratorHelpersMem1 = memoryBenchmark('Iterator Helpers (filter + map + take) - 内存', () => {
  const result = dataset1
    .values()
    .filter(item => item.active)
    .map(item => ({ id: item.id, doubled: item.value * 2 }))
    .take(10)
    .toArray();
  
  return result;
});

const timeImprovement1 = ((arrayMethodTime1 - iteratorHelpersTime1) / arrayMethodTime1 * 100).toFixed(2);
const memImprovement1 = ((arrayMethodMem1 - iteratorHelpersMem1) / arrayMethodMem1 * 100).toFixed(2);
console.log(`\n性能提升: 时间 ${timeImprovement1}%, 内存 ${memImprovement1}%`);

// ============ 测试场景 2: 嵌套数据扁平化 ============

console.log('\n' + '='.repeat(60));
console.log('测试场景 2: 嵌套数据扁平化 + 过滤 + 取前 20 项');
console.log('数据规模: 10,000 个父项,每个包含 10 个子项');
console.log('='.repeat(60));

const dataset2 = Array.from({ length: 10000 }, (_, i) => ({
  id: i,
  children: Array.from({ length: 10 }, (_, j) => ({
    childId: `${i}-${j}`,
    value: Math.random() * 100
  }))
}));

// 传统数组方法 - 时间
const arrayMethodTime2 = benchmark('传统数组方法 (flatMap + filter + slice) - 时间', () => {
  const result = dataset2
    .flatMap(parent => parent.children)
    .filter(child => child.value > 50)
    .slice(0, 20);
  
  return result;
}, 10);

// 传统数组方法 - 内存
const arrayMethodMem2 = memoryBenchmark('传统数组方法 (flatMap + filter + slice) - 内存', () => {
  const result = dataset2
    .flatMap(parent => parent.children)
    .filter(child => child.value > 50)
    .slice(0, 20);
  
  return result;
});

// Iterator Helpers - 时间
const iteratorHelpersTime2 = benchmark('Iterator Helpers (flatMap + filter + take) - 时间', () => {
  const result = dataset2
    .values()
    .flatMap(parent => parent.children)
    .filter(child => child.value > 50)
    .take(20)
    .toArray();
  
  return result;
}, 10);

// Iterator Helpers - 内存
const iteratorHelpersMem2 = memoryBenchmark('Iterator Helpers (flatMap + filter + take) - 内存', () => {
  const result = dataset2
    .values()
    .flatMap(parent => parent.children)
    .filter(child => child.value > 50)
    .take(20)
    .toArray();
  
  return result;
});

const timeImprovement2 = ((arrayMethodTime2 - iteratorHelpersTime2) / arrayMethodTime2 * 100).toFixed(2);
const memImprovement2 = ((arrayMethodMem2 - iteratorHelpersMem2) / arrayMethodMem2 * 100).toFixed(2);
console.log(`\n性能提升: 时间 ${timeImprovement2}%, 内存 ${memImprovement2}%`);

// ============ 测试场景 3: 查找第一个匹配项 ============

console.log('\n' + '='.repeat(60));
console.log('测试场景 3: 查找第一个匹配项(匹配项在数据集后半部分)');
console.log('数据规模: 1,000,000 条');
console.log('='.repeat(60));

const dataset3 = generateLargeDataset(1000000);
const targetId = 800000;

// 传统数组方法 - 时间
const arrayMethodTime3 = benchmark('传统数组方法 (filter + [0]) - 时间', () => {
  const result = dataset3
    .filter(item => item.id === targetId)[0];
  
  return result;
}, 10);

// 传统数组方法 - 内存
const arrayMethodMem3 = memoryBenchmark('传统数组方法 (filter + [0]) - 内存', () => {
  const result = dataset3
    .filter(item => item.id === targetId)[0];
  
  return result;
});

// Iterator Helpers - 时间
const iteratorHelpersTime3 = benchmark('Iterator Helpers (find) - 时间', () => {
  const result = dataset3
    .values()
    .find(item => item.id === targetId);
  
  return result;
}, 10);

// Iterator Helpers - 内存
const iteratorHelpersMem3 = memoryBenchmark('Iterator Helpers (find) - 内存', () => {
  const result = dataset3
    .values()
    .find(item => item.id === targetId);
  
  return result;
});

const timeImprovement3 = ((arrayMethodTime3 - iteratorHelpersTime3) / arrayMethodTime3 * 100).toFixed(2);
const memImprovement3 = ((arrayMethodMem3 - iteratorHelpersMem3) / arrayMethodMem3 * 100).toFixed(2);
console.log(`\n性能提升: 时间 ${timeImprovement3}%, 内存 ${memImprovement3}%`);

// ============ 测试场景 4: 多次链式调用 ============

console.log('\n' + '='.repeat(60));
console.log('测试场景 4: 复杂链式调用(filter + map + filter + map + take)');
console.log('数据规模: 50,000 条');
console.log('='.repeat(60));

const dataset4 = generateLargeDataset(50000);

// 传统数组方法 - 时间
const arrayMethodTime4 = benchmark('传统数组方法 (多次链式调用) - 时间', () => {
  const result = dataset4
    .filter(item => item.active)
    .map(item => ({ ...item, doubled: item.value * 2 }))
    .filter(item => item.doubled > 500)
    .map(item => ({ id: item.id, final: item.doubled + 100 }))
    .slice(0, 15);
  
  return result;
}, 10);

// 传统数组方法 - 内存
const arrayMethodMem4 = memoryBenchmark('传统数组方法 (多次链式调用) - 内存', () => {
  const result = dataset4
    .filter(item => item.active)
    .map(item => ({ ...item, doubled: item.value * 2 }))
    .filter(item => item.doubled > 500)
    .map(item => ({ id: item.id, final: item.doubled + 100 }))
    .slice(0, 15);
  
  return result;
});

// Iterator Helpers - 时间
const iteratorHelpersTime4 = benchmark('Iterator Helpers (多次链式调用) - 时间', () => {
  const result = dataset4
    .values()
    .filter(item => item.active)
    .map(item => ({ ...item, doubled: item.value * 2 }))
    .filter(item => item.doubled > 500)
    .map(item => ({ id: item.id, final: item.doubled + 100 }))
    .take(15)
    .toArray();
  
  return result;
}, 10);

// Iterator Helpers - 内存
const iteratorHelpersMem4 = memoryBenchmark('Iterator Helpers (多次链式调用) - 内存', () => {
  const result = dataset4
    .values()
    .filter(item => item.active)
    .map(item => ({ ...item, doubled: item.value * 2 }))
    .filter(item => item.doubled > 500)
    .map(item => ({ id: item.id, final: item.doubled + 100 }))
    .take(15)
    .toArray();
  
  return result;
});

const timeImprovement4 = ((arrayMethodTime4 - iteratorHelpersTime4) / arrayMethodTime4 * 100).toFixed(2);
const memImprovement4 = ((arrayMethodMem4 - iteratorHelpersMem4) / arrayMethodMem4 * 100).toFixed(2);
console.log(`\n性能提升: 时间 ${timeImprovement4}%, 内存 ${memImprovement4}%`);

// ============ 总结 ============

console.log('\n' + '='.repeat(60));
console.log('总结');
console.log('='.repeat(60));
console.log(`
场景 1 (过滤+转换+取前N项):
  时间提升: ${timeImprovement1}%
  内存提升: ${memImprovement1}%

场景 2 (嵌套数据扁平化):
  时间提升: ${timeImprovement2}%
  内存提升: ${memImprovement2}%

场景 3 (查找第一个匹配项):
  时间提升: ${timeImprovement3}%
  内存提升: ${memImprovement3}%

场景 4 (复杂链式调用):
  时间提升: ${timeImprovement4}%
  内存提升: ${memImprovement4}%

结论:
- Iterator Helpers 在需要"取前 N 项"的场景下优势明显
- 时间和空间开销都能显著降低
- 数据规模越大,提升越显著
- 惰性求值避免了不必要的中间数组创建
- 在查找场景下,Iterator Helpers 可以提前终止,避免遍历整个数组
`);

console.log('\n提示: 运行时加上 --expose-gc 参数可以获得更准确的内存测试结果');
console.log('命令: node --expose-gc iterator-helpers-benchmark.js');

实际运行输出

在我的环境(Node.js 22+)下,使用 node --expose-gc iterator-helpers-benchmark.js 运行上面的代码,得到以下结果:

============================================================
测试场景 1: 大数据集过滤 + 转换 + 取前 10 项
数据规模: 100,000 条
============================================================

传统数组方法 (filter + map + slice) - 时间:
  平均: 1.545ms
  最小: 0.920ms
  最大: 2.993ms
  内存增长: 3.38 MB

Iterator Helpers (filter + map + take) - 时间:
  平均: 0.019ms
  最小: 0.003ms
  最大: 0.126ms
  内存增长: 0.01 MB

性能提升: 时间 98.77%, 内存 99.75%

============================================================
测试场景 2: 嵌套数据扁平化 + 过滤 + 取前 20 项
数据规模: 10,000 个父项,每个包含 10 个子项
============================================================

传统数组方法 (flatMap + filter + slice) - 时间:
  平均: 3.716ms
  最小: 2.263ms
  最大: 8.936ms
  内存增长: 4.03 MB

Iterator Helpers (flatMap + filter + take) - 时间:
  平均: 0.018ms
  最小: 0.004ms
  最大: 0.120ms
  内存增长: 0.01 MB

性能提升: 时间 99.53%, 内存 99.73%

============================================================
测试场景 3: 查找第一个匹配项(匹配项在数据集后半部分)
数据规模: 1,000,000 条
============================================================

传统数组方法 (filter + [0]) - 时间:
  平均: 6.587ms
  最小: 5.605ms
  最大: 11.103ms
  内存增长: 0.01 MB

Iterator Helpers (find) - 时间:
  平均: 8.713ms
  最小: 7.893ms
  最大: 9.545ms
  内存增长: 14.80 MB

性能提升: 时间 -32.27%, 内存 -194800.20%

============================================================
测试场景 4: 复杂链式调用(filter + map + filter + map + take)
数据规模: 50,000 条
============================================================

传统数组方法 (多次链式调用) - 时间:
  平均: 1.421ms
  最小: 0.840ms
  最大: 2.797ms
  内存增长: 5.33 MB

Iterator Helpers (多次链式调用) - 时间:
  平均: 0.028ms
  最小: 0.005ms
  最大: 0.220ms
  内存增长: 0.01 MB

性能提升: 时间 98.02%, 内存 99.75%

============================================================
总结
============================================================

场景 1 (过滤+转换+取前N项):
  时间提升: 98.77%
  内存提升: 99.75%

场景 2 (嵌套数据扁平化):
  时间提升: 99.53%
  内存提升: 99.73%

场景 3 (查找第一个匹配项):
  时间提升: -32.27%
  内存提升: -194800.20%

场景 4 (复杂链式调用):
  时间提升: 98.02%
  内存提升: 99.75%

结论:
- Iterator Helpers 在需要"取前 N 项"的场景下优势明显
- 时间和空间开销都能显著降低
- 数据规模越大,提升越显著
- 惰性求值避免了不必要的中间数组创建
- 在查找场景下,Iterator Helpers 可以提前终止,避免遍历整个数组

提示: 运行时加上 --expose-gc 参数可以获得更准确的内存测试结果
命令: node --expose-gc iterator-helpers-benchmark.js

从输出可以看到:

场景 1-2、4("取前 N 项"类场景)

  • 时间提升:98%+
  • 内存提升:99%+
  • Iterator Helpers 在时间和空间上都有压倒性优势

场景 3(查找匹配项)

  • 时间下降:32%(Iterator Helpers 更慢)
  • 内存异常:这个结果看起来有问题,可能是 GC 时机导致的测量误差
  • 这个场景不适合用 Iterator Helpers

关键发现

  1. 内存优势极其明显:在适合的场景下,Iterator Helpers 的内存使用只有传统方法的 0.3%
  2. 中间数组是大头:场景 4 创建了 4 个中间数组,占用 5.33 MB;Iterator Helpers 几乎为零
  3. 不是所有场景都适用:场景 3 证明了 Iterator Helpers 不是银弹

如果你觉得这篇文章有帮助,欢迎关注我的 GitHub,下面是我的一些开源项目:

Claude Code Skills(按需加载,意图自动识别,不浪费 token,介绍文章):

qwen/gemini/claude - cli 原理学习网站

coding-cli-guide

全栈项目(适合学习现代技术栈):

  • prompt-vault - Prompt 管理器,用的都是最新的技术栈,适合用来学习了解最新的前端全栈开发范式:Next.js 15 + React 19 + tRPC 11 + Supabase 全栈示例,clone 下来配个免费 Supabase 就能跑
  • chat_edit - 双模式 AI 应用(聊天+富文本编辑),Vue 3.5 + TypeScript + Vite 5 + Quill 2.0 + IndexedDB

VS Code 插件

  • vscode-ai-commit - 一键生成 commit message,支持 Conventional Commits,兼容任何 OpenAI 格式接口
❌