网站首页 > 技术文章 正文
迭代器(iterator)是一个结构化的模式,用于从源以一次一个的方式提取数据。迭代器的使用可以极大地简化数据操作,于是ES6也向JS中添加了这个迭代器特性。新的数组方法和新的集合类型(如Set集合与Map集合)都依赖迭代器的实现,这个新特性对于高效的数据处理而言是不可或缺的,在语言的其他特性中也都有迭代器的身影:新的for-of循环、展开运算符(...),甚至连异步编程都可以使用迭代器。
今天笔者将从以下几个方面进行介绍迭代器:
- 什么是迭代器(iterator)?
- 基于协议实现迭代器
- 迭代器的应用
本篇文章阅读时间预计6分钟。
迭代器(iterator)
迭代器是一种有序、连续的、基于拉取的用于消耗数据的组织方式,用于以一次一步的方式控制行为。简单的来说我们迭代循环一个可迭代对象,不是一次返回所有数据,而是调用相关方法分次进行返回。
迭代器iterator是一个object,这个object有一个next函数,该函数返回一个有value和done属性的object,其中value指向迭代序列中当前next函数定义的值。
ES6的迭代协议分为迭代器协议(iterator protocol)和可迭代协议(iterable protocol),迭代器基于这两个协议进行实现。
迭代器协议: iterator协议定义了产生value序列的一种标准方法。只要实现符合要求的next函数,该对象就是一个迭代器。相当遍历数据结构元素的指针,类似数据库中的游标。
可迭代协议: 一旦支持可迭代协议,意味着该对象可以用for-of来遍历,可以用来定义或者定制JS 对象的迭代行为。常见的内建类型比如Array & Map都是支持可迭代协议的。对象必须实现@@iterator方法,意味着对象必须有一个带有@@iterator key的可以通过常量Symbol.iterator访问到的属性。
下图展示了arrays,Maps,Strings数据类型实现了可迭代协议,我们可以使用for-of和展开语法显示迭代器的数据。
基于协议实现迭代器
迭代器协议
如下代码展示了基于迭代协议进行实现:
let obj = { array: [1, 2, 3, 4, 5], nextIndex: 0, next: function() { return this.nextIndex < this.array.length ? {value: this.array[this.nextIndex++], done: false} : {done: true} } }; console.log(obj.next().value); console.log(obj.next().value); console.log(obj.next().value); console.log(obj.next().value); console.log(obj.next().value); console.log(obj.next().done);
上述代码将会输出
1 2 3 4 5 true
上述代码的next方法还可以按如下代码进行改写,看起来更清晰些:
if(this.nextIndex < this.array.length) { this.nextIndex++; return { value: this.array[this.nextIndex], done: false } } else { return { done: true } }
我们可以看出,next方法的实现,如果存在新的元素,返回当前元素的并将当前元素位置的标识递增加1,当没有元素时,返回{ done: true }。
可迭代协议
根据可迭代协议,对象需要提供@@iterator方法; 也就是说,它必须将Symbol.iterator符号作为属性键。 @@iterator方法必须返回迭代器对象。代码实现如下:
let obj = { array: [1, 2, 3, 4, 5], nextIndex: 0, [Symbol.iterator]: function(){ return { array: this.array, nextIndex: this.nextIndex, next: function(){ return this.nextIndex < this.array.length ? {value: this.array[this.nextIndex++], done: false} : {done: true}; } } } }; let iterable = obj[Symbol.iterator]() console.log(iterable.next().value); console.log(iterable.next().value); console.log(iterable.next().value); console.log(iterable.next().value); console.log(iterable.next().value); console.log(iterable.next().done);
上述代码将会输出
1 2 3 4 5 true
上述代码,我们实现了自定义的迭代器,基于JS的作用域和闭包特性才能轻松实现。arrays,Maps,Strings数据类型实现了可迭代协议,其 __proto__原型链指向自带Symbol.iterator的方法,节省了我们手写代码的时间,如下代码所示:
const arr = [1, 2]; const iterator = arr[Symbol.iterator](); // returns you an iterator console.log(iterator.next()) console.log(iterator.next()) console.log(iterator.next())
上述代码将会输出:
{ value: 1, done: false } { value: 2, done: false } { value: undefined, done: true }
我们可以使用for-of,展开语法迭代数组,示例代码如下:
const arr = [1, 2]; for(var v of arr){ console.log(v); } //outputs 1 //outputs 2 console.log([...arr]); //outputs[1,2];
obj对象没有实现可迭代协议,我们如何迭代obj对象呢?实现obj的迭代器呢,示例代码如下:
var obj={ a:1, b:2, c:3, [Symbol.iterator]:function () { var keys=Object.keys(this);//object.vulues(this) var index=0; return{ next:()=> (index<keys.length)? {value: this[keys[index++]], done:false} :{done: true,value:undefined} } } }; console.log([...obj]); //outputs [1,2,3]
迭代器应用
斐波那契数列
斐波那契数列(Fibonacci sequence),又称黄金分割数列、因数学家列昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:1、1、2、3、5、8、13、21、34、……在数学上,斐波纳契数列以如下被以递推的方法定义:F(1)=1,F(2)=1, F(n)=F(n-1)+F(n-2)(n>=3,n∈N*)。
我们可以使用迭代器来产生一个序列数不大于100的斐波那契数列,示例代码如下:
let Fib= { [Symbol.iterator](){ let n1=1; let n2=1; let max=100; return { next(){ let current=n2; n2=n1; n1=n1+current; if(current<max){ return {value: current, done: false} }else{ return { done: true} } } } } } console.log([...Fib]); //outputs [ 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 ]
模拟任务队列
有时候我们需要要执行任务放在一个队列里,分次执行,我们可以使用迭代器进行模拟,示例代码如下:
let tasks={ actions:[], [Symbol.iterator](){ let steps=this.actions.slice(); return { [Symbol.iterator]() {return this;}, next(...args){ if(steps.length>0){ let res=steps.shift()(...args); return {value:res,done:false}; } else{ return {done:true} } }, return(v){ steps.length=0; return {value:v,done:true}; } }; } }; tasks.actions.push( function step1(x) { console.log("step 1:",x); return x*2; }, function step2(x,y) { console.log("step 2:",x,y); return x+(y*2); }, function step3(x,y,z) { console.log("step 3:",x,y,z); return (x*y)+z; } ); let it=tasks[Symbol.iterator](); console.log(it.next(10)); console.log(it.next(20,50)); console.log(it.next(20,50,120)); console.log(it.next());
上述代码输出:
step 1: 10 { value: 20, done: false } step 2: 20 50 { value: 120, done: false } step 3: 20 50 120 { value: 1120, done: false } { done: true }
从上述代码,我们可以看出,迭代器不仅仅是数据的迭代,还可以作为一个模式来组织相关的功能。(注:本示例来源《你不知道的JavaScript》下卷)
小节
今天的内容就到这里,迭代器是不是很神奇,好像如魔法一般,我们随意控制函数的中断与继续,丰富了我们解决问题的思路,让我们的代码看起来更加工程化和结构化,提高了代码的可读性和可理解性。
更多精彩内容,请微信关注“前端达人”公众号!
猜你喜欢
- 2025-05-26 ES6字符串的新特性及新增方法梳理
- 2025-05-26 ES6新增语法(七)——async...await
- 2024-09-24 前端ES6中Promise的运行原理
- 2024-09-24 「ES6基础」Object的新方法
- 2024-09-24 「ES6基础」const的用法
- 2024-09-24 「ES6基础」箭头函数(Arrow functions)
- 2024-09-24 前端关于ES6的面试题汇总含答案
- 2024-09-24 前端系列——ES6中的数据结构map和set
- 2024-09-24 前端ES6数组方法总结
- 2024-09-24 前端ES6中Promise的运行原理之进阶篇(一)
你 发表评论:
欢迎- 05-30为什么说网上的md5加密解密站都是通过彩虹表解密的?
- 05-30一文读懂md5,md5有什么用,什么是md5加盐
- 05-30Java md5加密解密数据
- 05-30MD5是什么?如何进行MD5校验?
- 05-30专家教你简单又轻松的MD5解密方法,一看就会
- 05-30多学习才能多赚钱之:vscode怎么安装插件
- 05-30VSCode无限画布模式(可能会惊艳到你的一个小功能)
- 05-30VSCode神级Ai插件Cline:从安装到实战【创建微信小程序扫雷】
- 最近发表
- 标签列表
-
- 前端设计模式 (75)
- 前端性能优化 (51)
- 前端模板 (66)
- 前端跨域 (52)
- 前端缓存 (63)
- 前端react (48)
- 前端aes加密 (58)
- 前端脚手架 (56)
- 前端md5加密 (54)
- 前端路由 (55)
- 前端数组 (65)
- 前端定时器 (47)
- Oracle RAC (73)
- oracle恢复 (76)
- oracle 删除表 (48)
- oracle 用户名 (74)
- oracle 工具 (55)
- oracle 内存 (50)
- oracle 导出表 (57)
- oracle 中文 (51)
- oracle链接 (47)
- oracle的函数 (57)
- mac oracle (47)
- 前端调试 (52)
- 前端登录页面 (48)
本文暂时没有评论,来添加一个吧(●'◡'●)