【js】迭代器与生成器
迭代器(Iterator)
有时也称为遍历器 - 迭代器对象
作用
- 为各种数据结构提供
统一的访问接口
- 使数据结构的成员按照
某种次序排列
- 统一的迭代方式for...of循环
Symbol.iterator
属性的数据结构是可迭代的Symbol.iterator:值为迭代器生成函数
原生可迭代的数据结构:
- Array
- Map
- Set
- String
- TypedArray
- arguments
- NodeList
这些数据结构均可以使用for...of来遍历
//以数组为例,判断是否有Symbol.iterator属性
const arr = [1, 2, 3, 4]
Symbol.iterator in arr //true
//for...of遍历数组
for(const i of arr){
console.log(i) //1 2 3 4
}
2.迭代器生成函数(Symbole.iterator) 【【js】迭代器与生成器】返回一个
迭代器对象
const arr = [1,2,3,4]
console.log(arr[Symbol.iterator]())
文章图片
迭代器中有一个2.1next()方法next函数
next()会返回一个成员对象,有value和done两个属性,value表示当前成员
的值,done表示遍历是否终止
for...of每次的遍历都会调用next函数并取value
const arr = [1, 2, 3, 4]
let iter = arr[Symbol.iterator]() //返回一个遍历器对象
console.log(iter.next());
//{value: 1, done: false}
console.log(iter.next());
//{value: 2, done: false}
console.log(iter.next());
//{value: 3, done: false}
console.log(iter.next());
//{value: 4, done: false}
console.log(iter.next());
//{value: undefined, done: true}
遍历器对象可以视为一个指针,每次调用上面的next方法都会移动指针2.2实现一个迭代器生成函数
数组
//实现
function makeIterator(arr) {
var nextIndex = 0;
return {
next() {
return nextIndex < arr.length ?
{ value: arr[nextIndex++], done: false } :
{ value: undefined, done: true }
}
}
}
//测试
const arr = [1]
let iter = makeIterator(arr)
console.log(iter.next()) //{value: 1, done: false}
console.log(iter.next());
//{value: undefined, done: true}
- 对于对象也可以去实现一个迭代器生成函数,思路大致一样
- 但是对象上有丰富的方法间接生成一个数组,Object.values(obj)、keys、entries,这样就间接拥有了Symbol.iterator接口,可以使用for...of循环遍历
- 对于对象这种非线性的数据结构,部署迭代器接口相当于将其转化为线性结构,不如直接使用Map
- 从语法和结构上看,封装了多个内部状态 -
状态机
- 从返回结果看,返回了一个迭代器对象 -
迭代器生成函数
- functon与函数名之间有一个星号
*
- 内部使用
yield
语句定义不同的内部状态
function* generator(){ yield 1; yield 2; return 3; }
调用生成器函数不会运行内部代码,会返回一个迭代器对象,上面有next方法
function* generator() {
console.log("a");
yield 1;
console.log("b");
return 2
console.log("c");
yield 3
}
let iter = generator()
console.log(iter);
文章图片
console.log(iter.next());
// "a" {value:1,done:false}
console.log(iter.next());
// "b" {value:2,done:true}
console.log(iter.next());
// {value:undefined,done:true}
第一次调用next会开始运行函数,从起始到第一个yield结束,返回yield后的状态1.2参数
再调用next时会从上一个yield语句后开始执行,直到遇到下一个yield
当执行到return时,返回的对象done属性为true,且后续代码不再执行,下一调用返回{value:undefined,done:true}
yield不会终止只会中断且返回的done始终是false
next方法中可以添加参数,作为上一个yield的值,yield本身没有值
function* generator(x) {
var y = 2 * (yield (x + 1));
var z = yield (y / 3);
return (x + y + z)
}
let iter = generator(5)
console.log(iter.next());
//{value: 6, done: false}
console.log(iter.next(12));
//{value: 8, done: false}
console.log(iter.next(13));
//{value: 42, done: true}
第一次返回结果为x + 1 => 5 + 1
第二次的返回结果y由第一个yield计算而来,其值为第二个next()中的参数 2*12/3 = 8
z的值由第二个yield的值,其值为第三个next()中的参数 5 + 24 + 13 = 42
function* generator() {
let val1 = yield 1
console.log(""val1);
let val2 = yield 2
console.log(val2);
}
let iter = generator()
iter.next("one")
iter.next("two") //第一个yield的值:two
iter.next("three")//第二个yield的值:three
2.yield语句
- 普通的yield语句可以中断执行,并且返回转态
- yield* 后可以跟随一个迭代器对象,相当于在生成器内调用另一个生成器
function* generator1() { yield 1 yield 2 } function* generator2() { yield 'a' yield* generator1() yield 'b' return 'c' } let gene2 = generator2() for (const i of gene2) { console.log(i)// 'a' 1 2 'b' }
在一个生产器yield* 执行另一个生成器,相当于将它的语句插入到该生成器中
return返回的done是true,for...of在遍历时不会返回其值 - 如果是yield后执行一个生成器,next返回的value是一个迭代器对象
function* generator1() {
yield 1
yield 2
}
function* generator2() {
yield 'a'
yield generator1()
}
let gene2 = generator2()
console.log(gene2.next());
console.log(gene2.next());
文章图片
3.Generator.prototype.throw() 3.1作用
外部抛出
错误,在内部捕获
function* generator() {
try {
yield 1
yield 2
yield 3
}
catch (e) {
console.log("内部捕获:" + e);
}
}
let gene = generator()
try {
console.log(gene.next());
gene.throw("第一次错误");
console.log(gene.next());
gene.throw("第二次错误");
console.log(gene.next());
gene.throw("第三次错误");
console.log(gene.next());
} catch (e) {
console.log("外部捕获:" + e);
}
文章图片
第一次调用throw会在内部的catch中捕获,try中的语句不再执行3.2返回值
出现错误后再调用next为终止状态的对象,说明生成器被终止了
由于生成器被终止,第二次错误会在外部
捕获,生成器彻底终止
,再调用上面的方法不再生效
throw返回下一个next的状态
function* generator() {
try {
console.log(1);
yield "in11"
console.log(2);
yield "in12"
}
catch (e) {
console.log("内部捕获1");
}console.log(3);
yield "out1"try {
console.log(4);
yield "in21"
console.log(5);
yield "in22"
}
catch (e) {
console.log("内部捕获2");
}console.log(6);
yield "out2"
yield "out3"
}
let gene = generator()
try {
console.log(gene.next());
//1 {value: 'in11', done: false}
console.log(gene.throw());
//内部捕获1 3 {value: 'out1', done: false}
console.log(gene.next());
//4 {value: 'in21', done: false}
console.log(gene.throw());
//内部捕获2 6 {value: 'out2', done: false}
console.log(gene.next());
//7 {value: 'out3', done: false}
console.log(gene.throw());
//外部捕获
console.log(gene.next());
} catch (e) {
console.log("外部捕获");
}
上面的代码中有多个try catch4.Generator.prototype.return() 返回给定的值,终止生成器
第一个next会进入第一个try中
第一个throw的调用环境在有catch,跳出try
执行至下一个yield
若下一个yield
有catch可继续throw,若没有catch
则在外部捕获
,生成器终止
throw在错误被捕获后
再去返回下一个next的返回值
function* generator(){
yield 1
yield 2
yield 3
}
let gene = generator()
gene.next() // {value:1,done:false}
gene.return("foo")// {value:"foo",done:false}
gene.next() // {value:undefined,done:false}
- 如果在try..finally中,return会在finally后运行
function* generator() { try { yield 1 yield 2 } finally { yield 3 yield 4 } yield 5 } let gene = generator() console.log(gene.next()); //{value: 1, done: false} console.log(gene.return(6)); //{value: 3, done: false} console.log(gene.next()); //{value: 4, done: false} console.log(gene.next()); //{value: 6, done: true} console.log(gene.next()); //{value: undefined, done: true}
第一个next进入try中,如果直接return会直接结束
在try中执行return,会进入finally中,执行至第一个yield,返回状态,并将return添加到finally的结尾
在finally中执行return会直接结束
function* iterEntries(obj) {
let keys = Object.keys(obj)
for (let i = 0;
i < keys.length;
i++) {
let key = keys[i];
yield [key, obj[key]]
}
}
let objEntr = iterEntries({ a: 1, b: 2, c: 3 })
for (const [key, val] of objEntr) {
console.log(key, val);
}
'a' 1
'b' 2
'c' 3
推荐阅读
- 宽容谁
- 我要做大厨
- 增长黑客的海盗法则
- 画画吗()
- 2019-02-13——今天谈梦想()
- 远去的风筝
- 三十年后的广场舞大爷
- 叙述作文
- 20190302|20190302 复盘翻盘
- 学无止境,人生还很长