TC39 將三個 Iterator 方法推進 Stage 4,不必再先轉陣列
tc39/proposals · 2026-09-29
處理 iterator 時,分批、搜尋、串接都得先 Array.from() 轉成陣列,這個繞路現在有了標準答案:Iterator.prototype.chunks/windows、includes、join 三個提案在 2026.09.29 的 TC39 會議後,全數進入 Stage 4。
三筆升級各有一個 commit:Iterator Chunking、Iterator Includes、Iterator Join,標題都寫「to stage 4, per 2026.09.29 TC39」。Stage 4 代表進入下一版 ECMAScript 規格。
原本的問題
iterator helpers 已有 map、filter、take、drop 等惰性方法,但只要想做「每 N 筆一組」、「有沒有這個值」、「串成字串」,就得離開 iterator。離開的做法是先轉成陣列,這會把整條串流物化進記憶體,也失去提早結束的機會。
Join 提案自己列出的現況就是兩種:Array.from(it).join(sep),或用 reduce 累加字串而付出中間字串的配置成本。Chunking 提案則指出,有些演算法需要同時看相鄰的多個元素,而 iterator 只能一次給一個。
三個方法各做什麼
chunks 與 windows 分別產生不重疊與重疊的分組,且提案頁明說最後不足的一組會保留,不會被截掉。
digits().chunks(2)
// [[0,1],[2,3],[4,5],[6,7],[8,9]]
digits().windows(2)
// [[0,1],[1,2],[2,3],...,[8,9]]includes 的簽名是 Iterator.prototype.includes(searchElement, fromIndex),比較規則沿用 Array.prototype.includes 的 SameValueZero。和陣列不同的是 fromIndex 不接受負數,要從後面找就先用 drop;迭代只會消耗到找到的元素或結束為止。
function* gen() { yield 1; yield 3; }
gen().includes(1); // true
gen().drop(1).includes(3); // true
gen().drop(2).includes(3); // falsejoin 是 Iterator.prototype.join(separator),語意比照 Array.prototype.join,但直接以 receiver 當 iterator 操作。提案頁列出的 champion 與作者都是 Kevin Gibbons;null/undefined 元素的細節提案頁沒有逐項說明,請以規格文字為準。
前後對照
| 需求 | 以前 | 之後 |
|---|---|---|
| 每 2 筆一組 | 自己寫 generator 或轉陣列後切片 | it.chunks(2) |
| 是否包含某值 | Array.from(it).includes(x) | it.includes(x) |
| 串成字串 | Array.from(it).join(sep) | it.join(sep) |
影響範圍
第一類是手寫分批、滑動視窗 generator 的程式,例如批次寫入、分頁、算移動平均的工具函式,之後可以換成標準方法並刪掉自己的版本。第二類是用 Array.from(...) 只為了呼叫 includes 或 join 的程式碼,改寫後不再需要整條序列的暫存陣列。
第三類要小心:如果專案有 polyfill 或在 Iterator.prototype 上自行掛了同名方法,行為可能與標準不同,例如 includes 的 fromIndex 不支援負數、chunks 保留不足的最後一組。升級前先對照規格,並跑一遍相關測試。
另外,撰稿時這三個提案的 README 仍標示 Stage 3,並寫著進一步推進要靠 2 個以上的出貨實作;各瀏覽器與 Node.js 何時帶出,release notes 尚未說明,正式使用前請查目標環境的支援狀況。
原始來源:Chunking commit、Includes commit、Join commit、chunking 提案、includes 提案、join 提案