JS中的迭代器和生成器
不算原创,更像是翻译,按照自己的理解组织了一下,内容主要来自Mozilla Iterators and Generators 以及页面里的相关扩展链接。
迭代器
定义
迭代器iterator是一个object,这个object有一个next函数,该函数返回一个有value和done属性的object,其中value指向迭代序列中的下个值。这样看来迭代器定义简直简单的感人T_T.
例如下列code
function makeIterator(array) {console.log("Enter this function");var nextIndex = 0;return {next: function() {return nextIndex < array.length ?{value: array[nextIndex++], done: false} :{done: true};}};
}var itt = makeIterator([1,3, 5]);console.log(itt);console.log("before calling next");console.log(itt.next().value);console.log(itt.next().value);console.log(itt.next().value);console.log(itt.next().done);
通过运行以上代码,我们可以看到打印结果是:
Enter this function
Object..
before calling next
1
3
5
true
其中打印Object内容展开是
Object
next:function ()
arguments:null
caller:null
length:0
name:”next”
prototype:Object
proto:function ()
[[FunctionLocation]]:testJS.js:31
[[Scopes]]:Scopes[2]
0:Closure (makeIterator)
array:Array(3)
nextIndex:3
1:Global
由此可以理解nextIndex和Array的值原来是存在闭包的scope里面。这个我之前一直不太理解的说,需要补一下闭包和scope相关知识。
迭代协议 Iteration Protocol
对迭代器有了初步概念后,该是时候稍作深入一下了,也就是迭代协议。迭代协议分为两部分:
- 可迭代协议
- 迭代器协议
可迭代协议(Iterable protocol)
What for?
可以认为,一旦支持可迭代协议,意味着该对象可以用for-of来遍历,可以用来定义或者定制JS 对象的迭代行为。常见的内建类型比如Array & Map都是支持可迭代协议的。
How?
对象必须实现@@interator方法,意味着对象必须有一个带有@@interator key的可以通过常量Symbol.iterator访问到的属性。[Symbol.iterator]是一个返回对象类型的零参数的函数。
可以通过运行下面代码来理解string对象的可迭代协议:
var someString = 'hi';
console.log(someString[Symbol.iterator]);
迭代器协议(iterator protocol)
迭代器协议就是已经在上面的迭代器一节所讲到的那样,iterator协议定义了产生value序列的一种标准方法。只要实现符合要求的next函数,该对象就是一个迭代器。
迭代协议例子
例如我们可以自定义string的迭代行为:
// need to construct a String object explicitly to avoid auto-boxing
var someString = new String('hi');someString[Symbol.iterator] = function() {return { // this is the iterator object, returning a single element, the string "bye"next: function() {if (this._first) {this._first = false;return { value: 'bye', done: false };} else {return { done: true };}},_first: true};
};
生成器(generator)
了解了什么是生成器之后,我们已经打好了基础,可以进一步理解生成器。
Why need it?
尽管自定义的迭代器很有用,不过创建迭代器需要仔细地编程,因为这里面需要明确地维护迭代中的内部状态。生成器可以允许你通过写一个可以维护自身状态数据的函数来定义一个可迭代算法,是迭代器的一个有力替代方案。
What is?
GeneratorFunction 是一种特殊类型的函数,该函数是作为iterator的工厂来工作的。当它得到执行的时候,会返回一个新的Generator object。
How to?
只要定义函数的时候使用 function * 语法就可以了。听起来生成器的写法也是简单的感人,那么事实是这样么……
code sample
function * makeGenerator(array){var index = 0;while (index <array.length)yield array[index++];
}var ge = makeGenerator(["a","b","c"]);
console.log(ge);
console.log(ge.next().value);
console.log(ge.next().value);
console.log(ge.next().value);
打印ge对象可以看到其proto, GeneratorFunction 和闭包scope的内容。
makeGenerator {[[GeneratorStatus]]: "suspended"}__proto__:Generator[[GeneratorStatus]]:"suspended"[[GeneratorFunction]]:function* makeGenerator(array)[[GeneratorReceiver]]:Window[[GeneratorLocation]]:testJS.js:53[[Scopes]]:Scopes[2]0:Closure (makeGenerator)array:Array(3)0:"a"1:"b"2:"c"length:3__proto__:Array(0)index:31:Global
那么理解了生成器的概念之后,查看打印的ge对象的内容,请问generator到底是迭代器还是可迭代呢?答案就是both。 那么为什么呢?可以在展开的ge对象里面 去找迭代协议的两种体现:next函数 和 Symbol.iterator属性。
可迭代
用户自定义可迭代User-defined Iterables
理解了上述概念后,我们就可以试着自己定义可迭代的对象啦。
例如用户定义一个可迭代的对象
var myIterable = {};
myIterable[Symbol.iterator] = function* () {yield 1;yield 2;yield 3;
};
for (let value of myIterable) { console.log(value);
}
内建可迭代(built-in iterables)
String, Array, TypedArray, Map and Set 对象都是内建的可迭代对象。
语法期待可迭代(Syntaxes Expecting Iterables)
不是很明白这个什么叫syntaxes expecting,不过好像不是很耽误。某些表达式或者语句是expecting iterables,例如下面四种
for-of 循环
用法如下,乍看好像跟for-in很像。
let list = [4, 5, 6];
for (let i of list) {console.log(i);
}
那么果真如此么?
例子如下:
let list = [4, 5, 6];
for (let i in list) {console.log(i); // "0", "1", "2",
}
for (let i of list) {console.log(i); // "4", "5", "6"
}
哈,蛮神奇的,果然不一样,这是为啥呢?
与for-in的区别: 在于for-in是遍历对象的所有枚举属性名字, for of 遍历对象的迭代器的values。
喜欢用python的同学不要跟我一样容易用混了哦~
spread操作符
多元素或者变量展开。详见spread operator
也可以用作array的copy,但要注意copy是浅层copy哦。
yield *
yield * 被用于委派(delegate to)另外一个生成器或者可迭代对象。说起来感觉还是不如代码理解的直白,用代码理解如下:
function * g1(){yield 2;yield * 'xyz';console.log("after g1 y 2");return "this is a return of g1";
}
function *g2(){yield 1;var ret= yield * g1();console.log("yield * g1 = "+ret);yield 3;
}
var ii = g2();
console.log(ii.next().value);
console.log(ii.next().value);
console.log(ii.next().value);
console.log(ii.next().value);
console.log(ii.next().value);
console.log(ii.next().value);
结果输出是:
1
2
x
y
z
after g1 y 2
yield * g1 = this is a return of g1
3
yield * g1( ),表示当前迭代被委派到g1()这个生成器上,继续执行运行 yield 2,然后 遇到yield * ‘xyz’,因为 ‘xyz’可迭代,然后迭代继续被委派,于是得到x, y , z, 执行完后返回g1这个生成器向后执行,输出g1( ) 返回值后,yield 3.
如果把上面代码的yield * ‘xyz’ 改成yield ‘xyz’,就可以感受下yield vs. yield * 了。
1
2
xyz
after g1 y 2
yield * g1 = this is a return of g1
3
析构赋值(Destructuring assignment, available in ES6)
用于将数组的多个元素或者对象的属性解包(unpack)到不同变量。例如:
var a, b, rest;
[a, b] = [10, 20];
var o = {p: 42, q: true};
var {p, q} = o;
除此之外还有其他用途,例如变量交换:
var a = 1;
var b = 3;
[a, b] = [b, a];
用这个做交换,无需额外创建临时变量,那么到底它是如何实现的呢(不是XOR trick)?
有了解的可以分析一下不?
高级生成器
Generator.prototype.next()
生成器是按需来计算yielded的值的。next()方法也可以接受一个value,这个value可以用来修改生成器的中间状态。传递给next() 的value就会被当作是上一个yield 表达式返回的结果。
举斐波那契生成器的例子,使用next(x) 来重启这个序列。
function* fibonacci() {var fn1 = 0;var fn2 = 1;while (true) { var current = fn1;fn1 = fn2;fn2 = current + fn1;var reset = yield current;if (reset) {fn1 = 0;fn2 = 1;}}
}
var sequence = fibonacci();
console.log(sequence.next().value); // 0
console.log(sequence.next().value); // 1
console.log(sequence.next().value); // 1
console.log(sequence.next().value); // 2
console.log(sequence.next().value); // 3
console.log(sequence.next().value); // 5
console.log(sequence.next().value); // 8
console.log(sequence.next(true).value); // 0
console.log(sequence.next().value); // 1
console.log(sequence.next().value); // 1
console.log(sequence.next().value); // 2
Generator.prototype.return()
返回给定的值并且终结生成器。
function* gen() { yield 1;yield 2;yield 3;
}var g = gen();g.next(); // { value: 1, done: false }
g.return('foo'); // { value: "foo", done: true }
g.next(); // { value: undefined, done: true }
Generator.prototype.throw()
The throw() method resumes the execution of a generator by throwing an error into it and returns an object with two properties done and value.
我理解是跟next(value)有点像,只不过是向里面传递了一个exception,然后使得该生成器里面触发异常并返回一个带有done和value属性的object。
返回的done值根据运行时迭代器是否done而取true或者false。
例子如下:
function* gen() {while(true) {try {yield 42;} catch(e) {console.log('Error caught!');}}
}var g = gen();
console.log(g.next());
console.log(g.throw(new Error('Something went wrong')));
console.log(g.next());
打印结果是:
Object {value: 42, done: false}
Error caught!
Object {value: 42, done: false}
Object {value: 42, done: false}
JS中的迭代器和生成器相关推荐
- js中的迭代器与生成器(详解)
目录 一,迭代器 1.迭代器概念 2.迭代器的基本应用 3.迭代器原理 4.应用:自定义遍历数据 二,生成器 1.生成器概念 2.生成器的基本应用 3.yield表达式的值 4.应用:模拟获取数据 三 ...
- dataloader 源码_pytorch :: Dataloader中的迭代器和生成器应用
在使用pytorch训练模型,经常需要加载大量图片数据,因此pytorch提供了好用的数据加载工具Dataloader. 为了实现小批量循环读取大型数据集,在Dataloader类具体实现中,使用了迭 ...
- 掌握JavaScript中的迭代器和生成器,顺便了解一下async、await的原理
掌握JavaScript中的迭代器和生成器,顺便了解一下async.await的原理 目录 掌握JavaScript中的迭代器和生成器,顺便了解一下async.await的原理 前言 1.迭代器(It ...
- Python3 中打的迭代器与生成器
迭代器 迭代是Python最强大的功能之一,是访问集合元素的一种方式. 迭代器是一个可以记住遍历的位置的对象. 迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束.迭代器只能往前不会后退 ...
- Python中的迭代器和生成器
Python中的迭代器是非常实用的,但是迭代器只能应用在类序列对象上,比如列表.元组和字典.除非生成迭代器的类,否则无法在非类序列对象上调用next()方法. 自己可以定义迭代器的类,只要满足: 1. ...
- 【Python】Python中的迭代器和生成器
迭代器和生成器在很多编程语言中都会以不同形式的存在,在实际编程中经常会被用到.下面我们来了解一下什么是迭代. 1 迭代 了解Java的AIAS行者(AI行者是我B站粉丝勋章名称)应该都会知道,在Jav ...
- python 面试生成器和迭代器_Python 中的迭代器与生成器
导读 这篇文章主要介绍了 python 当中的迭代器与生成器,在涉及到大数量的场景应该考虑使用迭代器与生成器. 原文链接: https://russellgao.cn/python-iter/ 可迭代 ...
- python中的迭代器,生成器,闭包,装饰器,@property
一.迭代器 迭代器在Python中无处不在.它们在for循环,理解,生成器等中优雅地实现,但却隐藏在眼皮底下. Python中的Iterator只是一个可以迭代的对象.一个将返回数据的对象,一次返回一 ...
- Python中的迭代器,生成器(yield语句),正则表达式,re模块
目录 一.定义一个生成器: 二.模拟range函数的功能,自己建立一个range:MyRange 三.re模块中函数的使用(正则表达式) 一.定义一个生成器: 要求:生成1-10的数字,使用next( ...
- 简单介绍Python 中的迭代器和生成器
可迭代对象和迭代器 迭代(iterate)意味着重复,就像 for 循环迭代序列和字典那样,但实际上也可使用 for 循环迭代其他对象:实现了方法 __iter__ 的对象(迭代器协议的基础). __ ...
最新文章
- 刷新页面时间不重置 前端倒计时_brackets:前端开发工程师必备编辑器之一
- poj 3468 A Simple Problem with Integers 基础线段树
- python版本历史_python历史介绍
- Pyqt5_QPushButton
- 第7月第25天 xcode bundle calayer动画
- webserver总结
- LabVIEW编程LabVIEW开发Keithley 6485例程与相关资料
- 网络三定律:摩尔定律、吉尔德定律和迈特卡夫定律
- android imageview 锯齿,[置顶] android 自定义圆角ImageView以及锯齿的处理
- mysql cast 整数_Mysql-CAST/CONVERT 类型转换函数之 整型
- Out of memory: Kill process 解决
- 未来计算机的特点是什么,【简答题】未来计算机的发展将会呈现以下几个趋势:...
- matplotlib解决中文乱码问题,或者RuntimeWarning: Glyph 20154 missing from current font.
- linux下virtualbox使用
- 2020Android-高级面试题总结(附答案解析),kotlin语法印章类
- 史上最全航班号,航班信息爬虫,飞常准
- web网页设计期末课程大作业 HTML+CSS+JavaScript仿天猫购物商城设计实例 企业网站制作
- contig命令整理文件碎片
- 线性构成图标绘制样例
- 计算机系素描教材,美术入门级素描的掌握_计算机软件及应用_IT计算机_专业资料...
热门文章
- 数学建模之回归分析加例题详解(MATLAB实现)
- 基于Atmega128的售水机Proteus仿真
- 初二计算机会考图操作,初二年级信息技术会考-考试大纲
- 使用Word(Office 365)版本中的简历助手
- 青少年c语言培训,青少年信息学奥赛培优教程·入门篇(2020年01月)
- 【工具推荐】Adobe Reader 设置高亮颜色及深浅
- AvalonDock 2.0+Caliburn.Micro+MahApps.Metro实现Metro风格插件式系统(一)
- vs2019键盘钩子_Windows消息钩子
- 奔图打印linux驱动下载,奔图P3405D打印机驱动下载
- OpenCVForUnity色度图