ES6 学习笔记(十一)迭代器和生成器函数
1、前言
JavaScript提供了许多的方法来获取数组或者对象中的某个元素或者属性(迭代)。从以前的for循环到之后的filter、map再到后来的for...in和for...of的迭代机制。只要具有iterator接口的都可被迭代。
2、迭代器 Iterator
2.1 含义
迭代器(iterator)为各种数据结构,提供一个统一的、简便的访问接口,简单的说,迭代可以是数组或对象的遍历方式。它使得数据结构的成员能够按某种次序排列,如上面所说,只要部署 Iterator 接口,就可以完成遍历操作。即依次处理该数据结构的所有成员。
2.2 工作过程
- 首先,创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
- 第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员
- 第二次调用指针对象的next方法,指针就指向数据结构的第二个成员,以此类推。
- 不断调用指针对象的next方法,直到它指向数据结构的结束位置
2.3 Symbol.iterator属性
2.3.1 简介
ES6 规定,默认的 Iterator 接口部署在数据结构的Symbol.iterator属性。也就是说,只要某种数据结构具有iterator接口,就是可遍历的。常见的具有这一特点的数据结构有:Array、Map、Set、String、TypedArray(类型化的数组)、函数的 arguments 对象、NodeList 对象
2.3.2 示例1:数组的iterator示例
let arr = [1, 2, 3]
let iter = arr[Symbol.iterator]()
console.log(iter.next());
console.log(iter.next());
console.log(iter.next());
console.log(iter.next());
运行结果:
{ value: 1, done: false }
{ value: 2, done: false }
{ value: 3, done: false }
{ value: undefined, done: true }
其中,上面的iter返回的是一个迭代器对象,由于arr数组只有3个元素,所以经过3次的next()调用之后,第四次指针指到尾部了,已经结束了,所以获取的value值为undefined。迭代已完成,所以返回done。
2.3.3 对象的Iterator
一个对象如果要具备可被for...of循环调用的 Iterator 接口,就必须在Symbol.iterator的属性上部署遍历器生成方法(原型链上的对象具有该方法也可)。
2.3.4 示例2 实现iterator接口的自定义类示例
class RangeIterator {
constructor(start, stop, step) {
this.value = start;
this.stop = stop;
this.step = step;
}
[Symbol.iterator]() {
return this;
}
next() {
let value = this.value;
if (value < this.stop) {
this.value += this.step;
return { done: false, value: value };
}
return { done: true, value: undefined };
}
}
function range(start, stop, step = 1) {
return new RangeIterator(start, stop, step);
}
for (let value of range(0, 9, 2)) {
console.log(value);
}
输出结果:
0
2
4
6
8
2.3.5 示例3 为对象添加iterator示例
// 给对象添加iterator接口
let obj = {
data: [1, 2, 3, 4, 5, 6],
[Symbol.iterator]() {
const self = this;
let index = 0;
return {
next() {
if (index < self.data.length) {
return {
value: self.data[index++],
done: false
}
} else {
return {
value: undefined,
done: true
}
}
}
}
}
}
let sum = 0
for (let d of obj) {
sum += d
}
console.log(sum);
输出结果:
21
3、生成器 Generator
3.1、含义
在前面的文章 中也提到过生成器,举个最简单的例子:
let gen = function* () {
yield 1;
yield 2;
yield 3;
}
for (let n of gen()) {
console.log(n);
}
很明显,生成器的函数的function后面有个*,函数中存在yield 关键字,在函数中,通过gen()进行函数调用并生成控制器,在这里是通过循环执行函数的。
再举个例子
let obj = {
name: "Tom",
age: 20,
*[Symbol.iterator]() {
yield this.name;
yield this.age;
}
}
console.log(obj[Symbol.iterator]().next());
console.log([...obj]);
for (let k of obj) {
console.log(k);
}
输出结果:
{ value: 'Tom', done: false }
[ 'Tom', 20 ]
Tom
20
可以看到,Generator 函数返回的遍历器对象,只有调用next方法才会遍历下一个内部状态,所以其实提供了一种可以暂停执行的函数。yield表达式就是暂停标志。
3.2 工作过程
遍历器对象的next方法的运行逻辑:
- 遇到yield表达式,就暂停执行后面的操作,并将紧跟在yield后面的那个表达式的值,作为返回的对象的value属性值。
- 下一次调用next方法时,再继续往下执行,直到遇到下一个yield表达式。
- 如果没有再遇到新的yield表达式,就一直运行到函数结束,直到return语句为止,并将return语句后面的表达式的值,作为返回的对象的value属性值。
- 如果该函数没有return语句,则返回的对象的value属性值为undefined。
3.3 Generator 函数的return方法
举个例子:
// 生成器函数
let gen = function* () {
yield 1;
yield 2;
yield 3;
return 'ok'
}
let g = gen()
console.log(g.next());
console.log(g.next());
console.log(g.next());
console.log(g.next());
console.log(g.next());
输出结果:
{ value: 1, done: false }
{ value: 2, done: false }
{ value: 3, done: false }
{ value: 'ok', done: true }
{ value: undefined, done: true }
由于在遍历到第三个next()时循环就已经结束了。再次遍历返回OK,done为true,再次遍历,由于指针已经到队列末尾,所以值为undefined。
如果for...of循环提前退出(通常是因为出错,或者有break语句),就会调用return方法。如果一个对象在完成遍历前,需要清理或释放资源,就可以部署return方法。
3.4 示例:定义类Fib,实现Fibonacci数列的获取
Fibonacci数列的后一个数等于前两个数之和
// 斐波拉契数列
class Fib {
constructor(num) {
this.num = num
}
*[Symbol.iterator]() {
let [a, b] = [0, 1]
while (true) {
[a, b] = [b, a + b]
if (a > this.num) return
yield a
}
}
}
let fibs = new Fib(100)
for (let f of fibs) {
console.log(f);
}
输出结果:
1
1
2
3
5
8
13
21
34
55
89
ES6 学习笔记(十一)迭代器和生成器函数的更多相关文章
- ES6学习笔记<二>arrow functions 箭头函数、template string、destructuring
接着上一篇的说. arrow functions 箭头函数 => 更便捷的函数声明 document.getElementById("click_1").onclick = ...
- python学习笔记四 迭代器,生成器,装饰器(基础篇)
迭代器 __iter__方法返回一个迭代器,它是具有__next__方法的对象.在调用__next__方法时,迭代器会返回它的下一个值,若__next__方法调用迭代器 没有值返回,就会引发一个Sto ...
- python3学习笔记10(迭代器和生成器)
参考http://www.runoob.com/python3/python3-iterator-generator.html 迭代器 迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束 ...
- Python学习笔记010_迭代器_生成器
迭代器 迭代就类似于循环,每次重复的过程被称为迭代的过程,每次迭代的结果将被用来作为下一次迭代的初始值,提供迭代方法的容器被称为迭代器. 常见的迭代器有 (列表.元祖.字典.字符串.文件 等),通常 ...
- python 3.x 学习笔记6 ( 迭代器 and 生成器 )
1.迭代器(Iterator): 可以被next()函数调用并不断返回下一个值的对象,成为迭代器:Iterator 可以直接用于for 循环的对象统称为可迭代对象:Iterable 迭代,顾名思 ...
- ES6学习笔记(四)—— async 函数
await 是 async wait 的简写, 是 generator 函数的语法糖. async 函数的特点: async 声明一个方法是异步的,await 则等待这个异步方法执行的完成 async ...
- ES6学习笔记十一:编程风格技巧
一:用let取代var 二:静态字符串一律使用单引号或反引号,不使用双引号.动态字符串(模版字符串)使用反引号. 三:解构赋值: 使用数组对变量赋值时,优先使用解构赋值: 函数的参数如果是对象的成员, ...
- ES6学习笔记(2)- 箭头函数
1. 箭头函数声明 箭头函数的声明方式示例: 1 const printValue = (condition) => { 2 let testValue = 55; 3 if (conditio ...
- ES6学习笔记<三> 生成器函数与yield
为什么要把这个内容拿出来单独做一篇学习笔记? 生成器函数比较重要,相对不是很容易理解,单独做一篇笔记详细聊一聊生成器函数. 标题为什么是生成器函数与yield? 生成器函数类似其他服务器端语音中的接口 ...
- ES6学习笔记<五> Module的操作——import、export、as
import export 这两个家伙对应的就是es6自己的 module功能. 我们之前写的Javascript一直都没有模块化的体系,无法将一个庞大的js工程拆分成一个个功能相对独立但相互依赖的小 ...
随机推荐
- what the difference betweent pin page and lock page ?
以前在项目中,大家为了避免自己使用的page被换出,使用的方式是mlock,从mlock的实现的看,它限制了page被swap, 然后在一个swap off的系统中,这样其实和mlock调用与否没有关 ...
- {版本发布公告}HMS Core 6.6.0来啦
分析服务 ◆ 留存分析支持¬将流失用户存为受众,开发者通过对流失人群的分层以及多维分析,在制定相关用户召回策略时将更有针对性: ◆ 原"受众分析"更名为"人群洞察&quo ...
- 使用STM32控制TMC5160驱动步进电机
首先先来了解一下TMC5160的3种工作模式 TMC5160通过两个引脚来控制它的工作模式:SD_MODE和SPI_MODE. 1.当SD_MODE接地,SPI_MODE拉高,TMC5160即工作在模 ...
- KingbaseES V8R3 备份恢复案例之--单实例环境sys_rman脚本备份案例
案例说明: sys_rman是KingbaseES数据库的物理备份工具,支持数据库的全备和增量备份,由于sys_rman工具使用需要配置多个参数,对于一般用户使用不是很方便.为方便用户在Kingbas ...
- 一文读懂,硬核 Apache DolphinScheduler3.0 源码解析
点亮 ️ Star · 照亮开源之路 https://github.com/apache/dolphinscheduler 本文目录 1 DolphinScheduler的设计与策略 1.1 分布 ...
- PHP函数小工具
PHP检测IP是否内网地址.保留地址 /** * @param string $ip 被检测的IP * @return bool 是否内网或者保留IP */ public function isInt ...
- 【gRPC】C++异步服务端客户端API实例及代码解析
对于同步API而言,程序的吞吐量并不高.因为在每次发送一个gRPC请求时,会阻塞整个线程,必须等待服务端的ack回到客户端才能继续运行或者发送下一个请求,因此异步API是提升程序吞吐量的必要手段. g ...
- flutter系列之:Material中的3D组件Card
目录 简介 Card详解 Card的使用 总结 简介 除了通用的组件之外,flutter还提供了两种风格的特殊组件,其中在Material风格中,有一个Card组件,可以很方便的绘制出卡片风格的界面, ...
- 获取 Docker 容器的 PID 号
# 获取容器的 CONTAINER ID docker ps -q 5354ce7e85e1 # 通过 docker top 获取 PID docker top 5354ce7e85e1 UID PI ...
- Kubernetes ConfigMap热更新
ConfigMap是用来存储配置文件的kubernetes资源对象,所有的配置内容都存储在etcd中. 总结 更新 ConfigMap 后: 使用该 ConfigMap 挂载的 Env 不会同步更新 ...