TC39JavaScript新語法Iterator迭代器

TC39 新語法深度解析(五):Iterator Helpers — 迭代器終於有了 map、filter、take

整理 TC39 Iterator Helpers(Stage 4)的 .map()、.filter()、.take()、.drop()、.flatMap() 等十個方法。lazy 迭代器如何讓大數據處理不需要創建中間陣列。

· 3 分鐘閱讀

這是 TC39 新語法深度解析系列的第五篇。過去,迭代器(Iterator)是 JavaScript 中「感覺應該很強大但實際上很少用」的物件——因為它沒有 mapfiltertake 這些常見方法。Iterator Helpers 改變了這一切。


過去的痛:迭代器有力量,但沒有工具

// 迭代器可以懒惰地處理數據(不需要先把所有數據放進記憶體)
// 但過去它沒有任何實用的方法...

const largeData = generateLargeDataset();  // 一百萬筆
const iterator = largeData[Symbol.iterator]();

// 想做這種事情?需要手動實現
function* map(iter, fn) {
  for (const item of iter) {
    yield fn(item);
  }
}

// Iterator Helpers 讓這一切變成原生方法

新方法速覽

// 十個新方法
iterator.map(fn)        // 映射
iterator.filter(fn)      // 過濾
iterator.take(n)         // 取前 n 個
iterator.drop(n)        // 跳過前 n 個
iterator.flatMap(fn)     // 展平 + 映射
iterator.reduce(fn, init) // 聚合
iterator.toArray()       // 轉成陣列
iterator.forEach(fn)     // 遍歷(副作用)
iterator.every(fn)       // 全都滿足?
iterator.some(fn)        // 有一個滿足?
iterator.find(fn)        // 找到第一個滿足的

take():處理大數據集

// 處理超大型資料時,只取需要的部分
function* generateFibonacci() {
  let a = 0, b = 1;
  while (true) {
    yield a;
    [a, b] = [b, a + b];
  }
}

// 只取前 20 個——不需要計算到一百萬
const first20 = generateFibonacci()
  .take(20)
  .toArray();
// [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181]

map + filter 組合:Lazy 流水線

// 過去:陣列方法會立刻創建中間陣列
const result = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  .filter(x => x % 2 === 0)  // 創建新陣列
  .map(x => x * 2);           // 再創建一個

// Iterator Helpers:沒有中間陣列——每個元素通過每個步驟後才到下一步
function* oddsAndSquares(n) {
  yield* n
    .filter(x => x % 2 !== 0)    // 一個一個處理
    .map(x => x * x);              // 沒有中間陣列
}

const result = [...oddsAndSquares([1, 2, 3, 4, 5])];
// [1, 9, 25]

實際應用:分頁 API 的懶惰處理

// 模擬分頁 API(一次拿 100 筆)
async function* fetchAllPages() {
  let page = 1;
  while (true) {
    const data = await fetch(`/api/items?page=${page}&limit=100`).then(r => r.json());
    if (data.length === 0) break;
    yield* data;
    page++;
  }
}

// 只取前 10 筆——實際上只會發送一個請求
const first10 = await fetchAllPages()
  .take(10)
  .toArray();

這個模式在處理分頁或大資料集時特別有價值——不需要把所有資料都載入記憶體,就可以在第一時間只取需要的部分


Iterator.from():把任何可迭代物件轉成迭代器

const arr = [1, 2, 3];
const iter = Iterator.from(arr);

const doubled = iter.map(x => x * 2).toArray();
// [2, 4, 6]

與 Array 方法的比較

場景Array 方法Iterator 方法
大資料集❌ 需要全部載入記憶體✅ 懶惰處理
多次轉換❌ 每步創建中間陣列✅ 一個元素通過每步
無限序列❌ 不可能take() 自動終止
回頭遍歷✅ 可以多次迭代❌ 只能一次

瀏覽器支援

所有主流瀏覽器 2024 版本(Chrome 122+、Firefox 127+、Safari 17.4+)都已支援。可以直接使用。


總結

Iterator Helpers 讓 JavaScript 的 Iterator 從「理論上存在」變成「實務上可用」:

// 從此告別手動實現 map/filter on iterator
function* map(iter, fn) {
  for (const item of iter) yield fn(item);
}

// 直接用原生語法
const result = Iterator.from(data).map(fn).filter(fn2).take(10).toArray();

這是 TC39 新語法深度解析系列的第五篇。下一篇:裝飾器語法正式標準化 — Decorators。


延伸閱讀


本文基於 TC39 Iterator Helpers 提案(Stage 4)整理。