news 2026/4/22 7:04:49

【迭代器】js 迭代器与可迭代对象终极详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【迭代器】js 迭代器与可迭代对象终极详解

目标:不仅会“用”,还能“设计、调试、扩展、优化”。文内包含从零手写、生成器、惰性管道、异步流、资源管理、常见坑、性能建议、练习清单等。


1. 核心协议

  • 可迭代协议 (Iterable):对象实现obj[Symbol.iterator](),返回一个迭代器。
  • 迭代器协议 (Iterator):返回值具备next()方法,每次next()返回{ value, done }
  • 消费方for...of、展开...、数组/对象解构、Promise.allnew Map(iterable)new Set(iterable)Array.from等。
  • 原生可迭代ArrayStringMapSetTypedArrayargumentsNodeList等。
constarr=[10,20];constit=arr[Symbol.iterator]();// 拿到迭代器console.log(it.next());// { value: 10, done: false }console.log(it.next());// { value: 20, done: false }console.log(it.next());// { value: undefined, done: true }

2. for…of / for…in / for await…of 对比

  • for...of:遍历“值”,依赖可迭代协议,顺序稳定。
  • for...in:遍历“可枚举属性键”,含原型链可枚举属性,不需要可迭代。
  • for await...of:遍历“异步可迭代”或“值为 Promise 的可迭代”,逐个await
constarr=[3,6,9];for(constvofarr)console.log('of =>',v);// 3 6 9for(constkinarr)console.log('in =>',k);// 0 1 2

3. 从零手写同步迭代器(含 return/throw)

场景:为自定义对象提供可迭代能力,并处理提前终止。

constcounter={current:1,max:3,[Symbol.iterator](){constself=this;return{next(){if(self.current<=self.max){return{value:self.current++,done:false};}return{value:undefined,done:true};},return(){console.log('迭代被提前终止,执行清理逻辑');return{value:undefined,done:true};},throw(err){console.log('外部抛错被捕获:',err.message);return{value:undefined,done:true};},};},};for(constnofcounter){console.log(n);if(n===2)break;// 触发 return()}

要点:

  • Symbol.iterator返回的对象必须实现next()
  • done: true视为终止;value可省略。
  • return()可用于break/return/throw时的清理;throw()让外部异常传入迭代器。

4. 生成器 (Generator) 深潜:function* / yield / yield*

生成器函数(function*/async function*)执行后返回一个“生成器对象”,它同时是迭代器和可迭代对象。生成器以“暂停/恢复”的方式运行,内部编译成状态机。

4.1 生成器函数 vs 生成器对象

  • 生成器函数:写法function* foo() { ... }const foo = function* () { ... };箭头函数不能写成生成器。
  • 生成器对象:调用生成器函数得到,如const it = foo();它拥有next/return/throw并实现Symbol.iterator
function*range(start,end,step=1){for(leti=start;i<=end;i+=step)yieldi;// yield 产出,并“暂停”}constit=range(1,3);console.log(it.next());// { value: 1, done: false }console.log(it.next());// { value: 2, done: false }console.log(it.next());// { value: 3, done: false }console.log(it.next());// { value: undefined, done: true }

4.2 yield 的双向通信与状态机

next(value)会把value作为“上一个 yield 表达式的结果”传回生成器内部。

function*dialog(){constname=yield'你是谁?';constlang=yield`你好,${name},你用什么语言?`;return`${name}使用${lang}`;}constg=dialog();console.log(g.next());// { value: '你是谁?', done: false }console.log(g.next('Alice'));// { value: '你好,Alice,你用什么语言?', done: false }console.log(g.next('JavaScript'));// { value: 'Alice 使用 JavaScript', done: true }

4.3 return / throw:主动收尾与异常注入

  • iter.return(value):立即终止,返回{ value, done: true },触发生成器内的finally
  • iter.throw(err):将错误注入生成器,在内部可被try/catch捕获;若未捕获则向外抛出。
function*work(){try{yield1;yield2;}finally{console.log('清理资源');}}constit2=work();console.log(it2.next());// { value:1, done:false }console.log(it2.return(99));// 清理资源 -> { value:99, done:true }

4.4 yield*:委托/扁平化子迭代器,并可接收子迭代器的 return

yield* otherIterable把“迭代控制权”交给子迭代器,等价于逐个for...of产出其值。yield*的结果是子迭代器的return值。

function*sub(){yield1;yield2;return9;// 会被 yield* 捕获}function*parent(){constret=yield*sub();// 产出 1、2,并获得 ret=9yieldret;// 再产出 return 值}console.log([...parent()]);// [1, 2, 9]
yield* 应用:递归/扁平化/管道组合
function*flatten(tree){for(constnodeoftree){if(Array.isArray(node))yield*flatten(node);// 递归委托elseyieldnode;}}console.log([...flatten([1,[2,[3,4]],5])]);// [1,2,3,4,5]

4.5 生成器的执行特性与调试要点

  • 惰性:直到调用next()才会继续运行;适合大/无限序列。
  • 单次消费:同一个生成器对象不可复位,需重新创建。
  • 清理:在生成器内部用try/finally;外部可以return()触发。
  • 不可用箭头函数:箭头语法不支持function*,需常规函数写法。
  • 与 for…offor...of自动反复next()直到done:truebreak/throw/return会触发迭代器的return()

5. 可迭代工具箱与常见 API

  • 展开/解构:[...iterable]const [a, ...rest] = iterable
  • 集合转换:Array.from(iterable)new Map(iterable)new Set(iterable)
  • Promise 组合:Promise.all(iterable)Promise.allSettledPromise.race(需可迭代)
constset=newSet([1,2,3]);constarr=[...set];// [1,2,3]const[first,...rest]=set;// first=1, rest=[2,3]

6. 自定义数据结构:可迭代的 Deque(类 + 私有字段)

classDeque{#data=[];pushFront(x){this.#data.unshift(x);}pushBack(x){this.#data.push(x);}popFront(){returnthis.#data.shift();}popBack(){returnthis.#data.pop();}getsize(){returnthis.#data.length;}[Symbol.iterator](){letidx=0;return{next:()=>idx<this.#data.length?{value:this.#data[idx++],done:false}:{value:undefined,done:true},return(){return{done:true};},};}}constdq=newDeque();dq.pushBack(10);dq.pushFront(5);dq.pushBack(20);for(constvofdq)console.log(v);// 5 10 20

设计建议:

  • 迭代期间若会修改内部存储,需明确顺序定义与终止条件(如记录快照或用生成器惰性遍历)。
  • 大数据/潜在无限序列优先用生成器,避免一次性展开耗内存。

7. 惰性管道:map / filter / take / drop

用生成器实现“按需取值”的流式组合。

function*map(iterable,fn){for(constxofiterable)yieldfn(x);}function*filter(iterable,pred){for(constxofiterable)if(pred(x))yieldx;}function*take(iterable,n){if(n<=0)return;leti=0;for(constxofiterable){yieldx;if(++i>=n)break;}}function*drop(iterable,n){leti=0;for(constxofiterable)if(i++>=n)yieldx;}constsrc=[1,2,3,4,5,6];constpipeline=take(filter(map(src,x=>x*3),x=>x%2===0),2);console.log([...pipeline]);// [6, 12]

优势:逐元素计算,适合大数据、IO 流;可轻松扩展更多算子(zip、flatMap、chunk、uniq 等)。


8. 异步迭代器与 for await…of

异步可迭代实现Symbol.asyncIteratornext()返回 Promise,或用async function*

constasyncCounter={current:1,max:3,async*[Symbol.asyncIterator](){while(this.current<=this.max){awaitnewPromise(r=>setTimeout(r,100));yieldthis.current++;}},};(async()=>{forawait(constnofasyncCounter)console.log(n);})();

典型场景:分页 API、网络流(ReadableStream)、文件流、数据库游标、消息队列。

同步可迭代 + Promise 元素

for await...of也能遍历“同步可迭代且元素为 Promise”的情况:

constxs=[1,2,3].map(v=>Promise.resolve(v*10));(async()=>{forawait(constvofxs)console.log(v);// 10 20 30})();

9. 资源管理与提前终止

在生成器中用try/finally+return()保障资源释放。

function*readChunks(reader){try{while(true){constchunk=reader.read();if(!chunk)break;yieldchunk;}}finally{reader.close();// 即便 break/throw 也会执行}}

在异步生成器中同理使用try/finally

asyncfunction*streamLines(stream){try{forawait(constlineofstream)yieldline;}finally{stream.destroy?.();}}

10. 常见坑排查表(含错误示例)

  • TypeError: object is not iterable:缺少Symbol.iterator或拼写错误。
  • 迭代器复用:多数迭代器是一次性的,复用要重新获取obj[Symbol.iterator]()
  • for...ofbreak/throw却未清理资源:实现return()或在生成器用finally
  • for...of误用在异步迭代器:应改for await...of
  • 隐式耗尽:[...iter]会一次性拉平,若是大数据/无限序列会卡死或 OOM;改用惰性消费。
  • 顺序期待:Set/Map保持插入顺序;普通对象属性遍历顺序有规则但不属“可迭代”。

调试技巧:

constit=someIterable[Symbol.iterator]();console.log(it.next(),it.next());// 手动探查序列

11. 性能与工程化建议

  • 惰性优先:未知大小或可能无限的来源用生成器/异步生成器。
  • 避免重复遍历:对昂贵来源(IO/计算)避免多次消费,可缓存结果或暴露toArray()
  • 批量/背压:异步流中可结合takechunkthrottle控制节奏。
  • 类型提示:在 TS 中为迭代器声明泛型,避免any扩散。
  • 组合优先:map/filter/take/drop/flatMap/zip 等算子小而精,利于单测和重用。
  • 清理保证:生成器里用try/finally;显式实现return()以防资源泄漏。

12. 典型模式示例

12.1 管道式数据流

function*flatMap(iterable,fn){for(constxofiterable){constres=fn(x);if(Symbol.iteratorinObject(res))yield*res;elseyieldres;}}constwords=['hi','js'];constchars=flatMap(words,w=>w.split(''));console.log([...chars]);// ['h','i','j','s']

12.2 无限序列 + take 限流

function*naturals(){leti=1;while(true)yieldi++;}console.log([...take(naturals(),5)]);// [1,2,3,4,5]

12.3 异步分页封装

asyncfunction*fetchPages(fetchPage){letpage=1;while(true){constdata=awaitfetchPage(page);if(!data.length)break;yielddata;page+=1;}}(async()=>{forawait(constpageoffetchPages(p=>api.list({page:p}))){console.log('page size',page.length);}})();

12.4 具备回收的文件读取(Node)

constfs=require('fs');asyncfunction*readLines(path){conststream=fs.createReadStream(path,'utf8');try{forawait(constchunkofstream)yieldchunk;}finally{stream.close();}}

13. FAQ 精要

  • 何时用生成器 vs 普通函数?需要“逐步产出/惰性/可中断/可组合”时用生成器。
  • 迭代器能重置吗?原生多数不可;若需可重复遍历,应在Symbol.iterator中返回“新的迭代器实例”。
  • 如何判断对象可迭代?obj != null && typeof obj[Symbol.iterator] === 'function'
  • async 迭代器如何并行?迭代本身是串行消费;并行可在内部批量启动 Promise,再逐个yield结果(注意背压)。
  • 能否在生成器里用 await?不能,改用async function*或在外层for await...of

15. 结语

迭代器与可迭代协议为 JS 提供统一、可组合的访问抽象;生成器/异步生成器进一步让“惰性、流式、可中断”变得自然。工程落地时,请同时关注资源释放、背压、可测试性与性能可观测性,把迭代封装成可靠的基础设施。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/18 5:14:40

OS.js实战指南:从零构建现代化Web桌面环境

OS.js实战指南&#xff1a;从零构建现代化Web桌面环境 【免费下载链接】OS.js OS.js - JavaScript Web Desktop Platform 项目地址: https://gitcode.com/gh_mirrors/os/OS.js 想象一下&#xff0c;你的团队需要一个统一的云端工作环境&#xff0c;员工无论身在何处都能…

作者头像 李华
网站建设 2026/4/18 3:43:54

性能优化关键策略:Ascend C Tiling(分块)机制原理解析

目录 摘要 1 引言&#xff1a;为什么Tiling是性能优化的核心&#xff1f; 1.1 硬件瓶颈的本质 1.2 Tiling的技术价值 2 Tiling技术原理深度解析 2.1 硬件架构与Tiling的数学基础 2.1.1 Tiling问题的形式化定义 2.1.2 多核负载均衡算法 2.2 Tiling策略分类与适用场景 …

作者头像 李华
网站建设 2026/4/17 7:38:06

如何用AI工具3步制作专业解说视频?零基础也能轻松上手

如何用AI工具3步制作专业解说视频&#xff1f;零基础也能轻松上手 【免费下载链接】NarratoAI 利用AI大模型&#xff0c;一键解说并剪辑视频&#xff1b; Using AI models to automatically provide commentary and edit videos with a single click. 项目地址: https://gitc…

作者头像 李华
网站建设 2026/4/20 17:15:56

milvus向量数据库使用尝试

一.背景在大语言模型&#xff08;LLM&#xff09;、计算机视觉、推荐系统等人工智能应用落地过程中&#xff0c;非结构化数据&#xff08;文本、图片、音频、视频&#xff09;的相似性检索成为核心需求 —— 这类数据需先通过模型转化为高维向量&#xff0c;再通过向量相似性计…

作者头像 李华
网站建设 2026/4/21 12:02:12

EasyGBS:一体化视频监控与智能管理解决方案

在数字化转型加速推进的背景下&#xff0c;视频监控已成为各行业安全管理、应急处置、运营优化的核心支撑手段。国标GB28181算法算力平台EasyGBS&#xff0c;凭借全协议兼容接入、全流程协同调度等核心能力&#xff0c;构建了一体化视频监控解决方案&#xff0c;广泛适配多样化…

作者头像 李华