JavaScript高级程序设计第四版--第七章--迭代器与生成器

在软件开发领域,"迭代"的意思是按照顺序反复多次执行一段程序,通常会有明确的终止条件。ECMAScript6规范新增了两个高级特性:迭代器和生成器。使用这两个特性,能够更清晰、高效、方便地实现迭代。

迭代器

迭代器是按需创建的一次性对象,每个迭代器都会关联一个可迭代对象(因此迭代器会阻止垃圾回收程序回收可迭代对象) ,并且暴露API(next方法)用于获取数据序列。暴露的API(通过next方法)需返回一个 IteratorResult对象用于表示下一个值是否还有下一个值是什么( {done: false|true; value: v; })

迭代器并不是与可迭代对象(数据序列)某个时刻的快照绑定

当循环语句提前退出循环后,再对迭代器进行循环会在上一次的基础上执行

kotlin 复制代码
class Counter{
    constructor(limit){
        this.count = 1;
        this.limit = limit;
    }
    next(){
        if(this.count <= this.limit){
            return {done:false,value:this.count++};
        }else{
            return {done:true,value:undefined};
        }
    }
    [Symbol.iterator](){
        return this;
    }
}

可迭代对象

定义:正式实现了Iterator接口的对象,并且通过Iterator方法返回迭代器的对象

javascript 复制代码
class Counter{
    constructor(limit){
        this.limit = limit;
    }
    [Symbol.iterator](){
        let count = 1, limit = this,limit;
        return {
            next(){
                if(count <= limit){
                    return {done:false, value:count++}
                }else{
                    return {done:true, value:undefined}
                }
            }
        }
    }
}

循环语句在迭代可迭代对象时,会调用其Symbol.iterator方法获得迭代器,并更具迭代器的值决定是否迭代终止。

javascript 复制代码
let testOb = {
        [Symbol.iterator]: () => {
          let i = 0;
          return {
            next: () => {
              i++;
              if (i < 10) {
                return { done: false, value: i };
              } else {
                return { done: true, value: undefined };
              }
            },
          };
        },
      };
for (const iterator of testOb) {
    console.log(iterator);
}

生成器

生成器是ECMAScript6新增的一个极为灵活的结构,拥有在一个函数块内暂停和恢复代码执行的能力。

生成器的形式是一个函数,在函数名称(箭头函数不能用来定义生成器)前面加一个星号(*)表示它是一个生成器。

javascript 复制代码
function* test() {  // 定义生成器函数
        yield 1;
        console.log("-----");
        yield 2;
      }
let testFunc = test();  // 调用生成器函数获得一个生成器对象,生成器函数并没有执行
console.log(testFunc)
console.log(testFunc === testFunc[Symbol.iterator]()) // 生成器对象实现了Iterable接口,它们默认的迭代器是自引用的
console.log(testFunc.next()); // 与迭代器相似,生成器对象也实现了Iterator接口,因此具有next方法,调用这个方法会让生成器开始或恢复执行。yield 1执行完后函数停止
console.log("++++++++");
console.log(testFunc.next()); // 从yield 1;之后的语句开始执行

上一次让生成器函数暂停的yield关键字会接收到传给next()方法的第一个值

lua 复制代码
function* generatorFn(initial){
        console.log(initial);
        console.log(yield); // yield不能用作计算
        console.log(yield);
      }
let generatorObject = generatorFn(1);
generatorObject.next(2) // 2不会被输出,因为这条执行后yield才开始暂停函数执行
generatorObject.next(3)
generatorObject.next(4)

可以使用星号(*)增强yield的行为,让它能够迭代一个可迭代对象,从而一次产生一个值

lua 复制代码
function* generatorFn(){
        yield *[1,2]
        console.log("--------------") 
        yield *[3,4]
      }
let generatorObject = generatorFn(); 
console.log(generatorObject.next())
console.log(generatorObject.next())
console.log(generatorObject.next())
console.log(generatorObject.next())

等价如下语句

vbnet 复制代码
function* generatorFn(){
        for (const iterator of [1,2]) {
          yield iterator
        }
        console.log("--------------") 
        for (const iterator of [3,4]) {
          yield iterator
        }
      }
let generatorObject = generatorFn();
console.log(generatorObject.next())
console.log(generatorObject.next())
console.log(generatorObject.next())
console.log(generatorObject.next())

return()

return方法会强制生成器进入关闭状态。提供给return方法的值,就是终止迭代器对象的值

javascript 复制代码
function* generatorFn(){
        for (const x of [1,2,3,4]) {
          yield x
        }
      }
const g = generatorFn();
console.log(g.next())     // {value: 1, done: false}
console.log(g.return(0))  // {value: 0, done: true}
console.log(g.next())     // {value: undefined, done: true}

throw()

throw方法会在暂停的时候将一个提供的错误注入到生成器对象中。如果错误未被处理,生成器就会关闭

javascript 复制代码
function* generatorFn() {
        for (const x of [1, 2, 3, 4]) {
          yield x;
        }
      }
const g = generatorFn();
console.log(g.next()); // {value: 1, done: false}
try {
	g.throw(0)
} catch (e) {
	console.log(e);       // 0
	console.log(g.next()); // {value: undefined, done: true}
}

如果生成器函数内部处理了这个错误,那么生成器就不会关闭,而且还可以恢复执行

javascript 复制代码
function* generatorFn() {
        for (const x of [1, 2, 3, 4]) {
          try {
            yield x;
          } catch (error) {
            console.log(error); // 0
            yield 0;
          }
        }
      }
const g = generatorFn();
console.log(g.next()); // {value: 1, done: false}
console.log(g.throw(0));  // {value: 0, done: false}
console.log(g.next()); // {value: 2, done: false}
console.log(g.next()); // {value: 3, done: false}
console.log(g.next()); // {value: 4, done: false}
console.log(g.next()); // {value: undefined, done: true}

如果生成器对象还没有开始执行调用throw抛出的错误不会在函数内部被捕获,因为这相当于在函数块外部抛出了错误

相关推荐
twins352039 分钟前
解决Vue应用中遇到路由刷新后出现 404 错误
前端·javascript·vue.js
qiyi.sky1 小时前
JavaWeb——Vue组件库Element(3/6):常见组件:Dialog对话框、Form表单(介绍、使用、实际效果)
前端·javascript·vue.js
煸橙干儿~~1 小时前
分析JS Crash(进程崩溃)
java·前端·javascript
安冬的码畜日常1 小时前
【D3.js in Action 3 精译_027】3.4 让 D3 数据适应屏幕(下)—— D3 分段比例尺的用法
前端·javascript·信息可视化·数据可视化·d3.js·d3比例尺·分段比例尺
l1x1n02 小时前
No.3 笔记 | Web安全基础:Web1.0 - 3.0 发展史
前端·http·html
昨天;明天。今天。2 小时前
案例-任务清单
前端·javascript·css
zqx_73 小时前
随记 前端框架React的初步认识
前端·react.js·前端框架
惜.己3 小时前
javaScript基础(8个案例+代码+效果图)
开发语言·前端·javascript·vscode·css3·html5
什么鬼昵称4 小时前
Pikachu-csrf-CSRF(get)
前端·csrf
长天一色4 小时前
【ECMAScript 从入门到进阶教程】第三部分:高级主题(高级函数与范式,元编程,正则表达式,性能优化)
服务器·开发语言·前端·javascript·性能优化·ecmascript