Javascript高级程序设计第七章 | ch7 | 阅读笔记
迭代器与生成器
在软件开发领域,”迭代“的意思是按照顺序反复多次执行一段程序
理解迭代
在JavaScript中,计数循环就是最简单的迭代
但是这种迭代有点问题:
1. 迭代之前需要事先知道使用何种数据结构
2. 遍历顺序并不是数据结构固有的
后面js实现了Array.prototype.forEach()向通用迭代迈出了一步
ES6实现了迭代器模式
迭代器模式
迭代器模式描述了一个方案,即把有些结构称为可迭代对象,因为它们实现了正式的Iterable接口,并且可以通过迭代器Iterator消费
可迭代对象不一定非得是集合对象,也可以是有类似数组行为的其他数据结构
每一个迭代器都会关联一个可迭代对象,而迭代器会暴露迭代其关联可迭代对象的API。迭代器无须了解与其关联的可迭代对象的结构,只需要知道如何取得连续的值。
可迭代协议
实现Iterator接口(可迭代协议)需要同时具备两种能力:
- 支持迭代的自我识别能力
- 创建实现Iterator接口的对象的能力
这意味着必须暴露一个属性作为默认迭代器,而且这个属性必须必须使用特殊的Symbol.iterator作为键。
我的理解:
上述提到实现了Iterable接口的对象称为可迭代对象,实际上可迭代对象实现的属性必须用Symbol.iterator作为属性,属性值是一个工厂函数,调用这个工厂函数,会返回一个新迭代器。
// 给Number实现的Iterator
let agg = 10;
Number.prototype[Symbol.iterator] = function() {
return {
next: function() {
return 'azoux iteration test'
}
}
}
ƒ () {
return {
next: function() {
return 'azoux iteration test'
}
}
}
agg[Symbol.iterator]()
{next: ƒ}
在实际写代码中不用显式地调用这个工厂函数来生成迭代器。实现可迭代协议的所有类型都会自动兼容接收可迭代对象的任何语言特性
// 特性:
// for of
// Promise.all
// Promise.race()
//yield
let arr = [1, 2, 3];
// 数组解构
let [a, b, c] = [1, 2, 3];
// 扩展运算符
let arr2 = [...arr];
// Array.from(arr)
// Set构造函数
// Map构造函数
迭代器协议
迭代器API使用next()方法在可迭代对象中遍历数据,每次成功调用都会返回一个IteratorResult对象
自定义迭代器
class Counter {
constructor(limit) {
this.count = 1;
this.limit = limit;
}
next() {
return this.count <= this.limit ? {
done: false,
value: this.count++
} : {
done: true,
value: undefined
};
}
[Symbol.iterator]() {
return this;
}
}
const counter = new Counter(3);
const it = counter[Symbol.iterator]();
console.log(it.next()); // { done: false, value: 1 }
for (let i of counter) console.log(i); // 2, 3
上面的类实现了Iterator接口,但是不太理想,因为它的每个实例都只能被迭代一次。
我们想要一个可迭代对象可以多次使用,因此可以考虑使用闭包,把计数器变量放到闭包中。
/**
* 自定义迭代器
*/
class Counter {
constructor(limit) {
this.limit = limit;
}
[Symbol.iterator]() {
let limit = this.limit;
let count = 1;
return {
next() {
return count <= limit ? {
done: false,
value: count++
} : {
done: true,
value: undefined
};
}
}
}
}
const counter = new Counter(3);
for (let i of counter) console.log(i); // 1, 2, 3
console.log('span'); //
for (let i of counter) console.log(i); // 1, 2, 3
提前终止迭代器
因为return()方法是可选的,所以并非所有迭代器都是可关闭的。
仅仅给一个不可关闭的迭代器加上return()方法并不能让它变成可关闭的
调用return()不会强制迭代器进入关闭状态
即便如此,return()方法还是会被调用
/**
* 自定义迭代器
*/
class Counter {
constructor(limit) {
this.limit = limit;
}
[Symbol.iterator]() {
let limit = this.limit;
let count = 1;
return {
next() {
return count <= limit ? {
done: false,
value: count++
} : {
done: true,
value: undefined
};
},
return () {
console.log('Exiting early!');
return {
done: true
};
}
}
}
}
let counter1 = new Counter(5);
for (let i of counter1) {
if (i > 2) break;
console.log(i);
}
// 1
// 2
// Exiting early!
let counter2 = new Counter(5);
try {
for (let i of counter2) {
if (i > 2) throw 'err';
console.log(i);
}
} catch (e) {
console.log(e);
}
// 1
// 2
// Exiting early!
// err
let arr = [1, 2, 3, 4, 5, 6, 7];
for (let i of arr) {
if (i > 2) break; // 1, 2
console.log(i);
}
console.log('---------------------'); // 1 2 3 4 5 6 7
for (let i of arr) console.log(i);
生成器
es6拥有在函数块内暂停和恢复代码执行的能力
生成器基础
- 生成器的形式是一个函数,函数名称前加上*号,来表示它是一个生成器。只要是能定义函数的地方都能定义生成器
- next方法的返回值类似迭代器,valued值默认为undefined,可以通过迭代函数返回值指定
- 生成器函数只会在初次调用next()方法后开始执行
/**
* 生成器定义
*/
let gen1 = function* () {
}
function* gen2 () {
}
// and so on....
function* genFunc() {
console.log('gen test');
return 'azoux';
}
let gen = genFunc();
console.log(gen); // genFunc {<suspended>}
console.log(gen.next()); // gen test {value: 'azoux', done: true}
console.log(gen); // genFunc {<closed>}
console.log(gen[Symbol.iterator]); // [Symbol.iterator]() { [native code] }
console.log(gen[Symbol.iterator]()); // genFunc {<closed>}
通过yield中断
- yield可以让生成器停止和开始执行
- 生成器函数在遇到yield之前会正常执行
- 遇到yield后会暂停执行,函数作用域的状态被保留,停止执行的生成器函数只能通过在生成器函数上调用next()方法来恢复执行
* yield不能嵌套使用。只能出现在生成器函数内部使用
*/
function* genFunc() {
yield 'azoux';
yield 'domy';
return 'the end';
}
const gen = genFunc();
console.log(gen.next()); // {value: 'azoux', done: false}
console.log(gen.next()); // {value: 'domy', done: false}
console.log(gen.next()); // {value: 'the end', done: true}
console.log(gen.next()); // {value: undefined, done: true}
/**
* 生成器对象作为可迭代对象
*/
for (const c of genFunc()) console.log(c);
// azoux
// domy
/**
* 使用yield实现输入输出
* yield关键之会接收传给next()方法的第一个值
* 第一次调用next()传入的值不会被使用,因为这一次调用是为了开始执行生成器函数
*/
function* genFunc(initial) {
console.log(initial);
console.log(yield);
console.log(yield);
}
let gen = genFunc('step 0'); // step 0
gen.next('step 1');
gen.next('step 2');
gen.next('step 3');
function* nTimes(n) {
for (let i = 0; i < n; i += 1) {
yield i;
}
}
for (let x of nTimes(5)) console.log(x);
/**
* 产生可迭代对象
* 用 * 增强yield的行为
* yield* 实际上就是把一个可迭代对象序列化为一连串可以单独产出的值
* yield* 的值是关联迭代器返回done: true时的value属性值
* 对于生成器函数的迭代器来说,这个值就是生成器函数自己return的值
*/
function* genFunc() {
yield*[1, 2, 3];
}
for (let k of genFunc())
console.log(k); // 1 2 3
function* innerGen() {
yield 'step 1';
yield 'step 2';
return 'step 3';
}
function* outerGen() {
console.log('iter value: ', yield* innerGen());
}
for (let k of outerGen()) {
console.log('value: ' + k);
}
// value: step 1
// value: step 2
// iter value: step 3
/**
* yield* 实现递归
*/
function* nTimes(n) {
if (n > 0) {
yield* nTimes(n - 1);
yield n - 1;
}
}
for (const c of nTimes(3)) console.log(c);
生成器作为默认迭代器
提前终止生成器
- return()
- throw()
Javascript高级程序设计第七章 | ch7 | 阅读笔记的更多相关文章
- javascript高级程序设计第三章的一些笔记
[TOC] 1. 语法 1.1 区分大小写 变量.函数名和操作费都区分大小写. 1.2 标识符 标识符指变量.函数.属性的名字,或者函数的参数.标识符按以下规则组合: 第一个字符必须是一个字母,下划线 ...
- 《JavaScript高级程序设计》——第二章在HTML使用JavaScript
这章讲的是JavaScript在HTML中的使用,也就是<script>元素的属性.书中详细讲了async.defer.src和type四个<script>的属性. 下面是对第 ...
- JavaScript 高级程序设计 第5章引用类型 笔记
第五章 引用类型 一.object类型 1.创建方法: 1.使用new 操作符创建 var person=new object() Person.name=”Nicholasa” Porson.age ...
- JavaScript高级程序设计第20章JSON 笔记 (学习笔记)
第二十章 JSON 1.Json 可以表示三种类型的值: 1.简单值: 表示数值:5 表示字符串:“hello wrold”注表示字符串时必须使用双引号 2.对象: {“name”:“mi”,”ag ...
- JavaScript高级程序设计第14章表单脚本 (学习笔记)
第十四章 表单脚本 1.阻止默认表单提交 1.提交表单数据 1.使用type=submit提交按钮 2.使用submit():方法 注意:当用户点击提交按钮时,会触发submit事件,从而在这里我们有 ...
- 《JAVASCRIPT高级程序设计》第一章
在使用调制解调器的时代,频繁的表单验证对客户端来说是一个很大的负担,javascript,作为一种专门进行表单验证的客户端脚本语言诞生了.到今天,javascript早已超越了当初设定的角色.Java ...
- 《JavaScript 高级程序设计》第一章:简介
JavaScript 历史 JavaScript的诞生的主要是当时的 netspace 公司谋求为自己的浏览器 Navigator 添加一种脚本语言,以便在本地客户端进行一些行为操作,而这一功能的需求 ...
- 《JavaScript高级程序设计》——第一章JavaScript简介
第一章主要讲了JavaScript的诞生和发展.刚刚接触JavaScript的我,似乎对这些内容并不感兴趣,快速看了一遍就开始去看第二章了. 看完第一章,收获也就是了解到JavaScript由ECMA ...
- javascript高级程序设计第5章,引用类型
object类型: 创建object实列的方式有两种,一种是new()方法,一种是对象字面量表示法: 第一种法方: var obj = new object(); obj.name = 'name' ...
- javascript高级程序设计第四章 变量、作用域和内存问题
变量包含两种,,基本类型和引用类型 基本类型是指一些简单的字段: 引用类型是☞由多个值构成的对象 引用类型的值是保存在内存中的对象,在javascript中是不允许直接访问内存中的位置; 函数的参数 ...
随机推荐
- Java Agent 踩坑之 appendToSystemClassLoaderSearch 问题
简介: 从 Java Agent 报错开始,到 JVM 原理,到 glibc 线程安全,再到 pthread tls,逐步探究 Java Agent 诡异报错. 作者:鲁严波 从 Java Age ...
- 深入解析 Dubbo 3.0 服务端暴露全流程
简介: 随着云原生时代的到来,Dubbo 3.0 的一个很重要的目标就是全面拥抱云原生.正因如此,Dubbo 3.0 为了能够更好的适配云原生,将原来的接口级服务发现机制演进为应用级服务发现机制. ...
- 2018-8-10-win10-uwp-如何开始写-uwp-程序
title author date CreateTime categories win10 uwp 如何开始写 uwp 程序 lindexi 2018-08-10 19:16:50 +0800 201 ...
- 笔记04_正确使用Heterogeneous元件
笔记04_正确使用Heterogeneous元件 1.出现错误的原因,就是一个元件的几个 部分没有分组.比如上一节创建的NE5532_HETE,当这个元件被调用两次或更多次时,存在若干个A,B部分,如 ...
- 【Python Web】flask视频流
这篇文档,完全借鉴miguelgrinberg的博客. https://blog.miguelgrinberg.com/post/flask-video-streaming-revisited 想看具 ...
- 海康iSC综合安防平台-视频web插件调试
综合安防管理平台 视频WEB插件 1.demo_window_simple_playback.html.demo_window_simple_preview.html为简化版demo,可在此基础上开发 ...
- 探索Django:从项目创建到图片上传的全方位指南
Django是什么 Django 是一个流行的 Python Web 开发框架,它提供了一系列工具和库,用于帮助开发人员构建高效.可扩展的 Web 应用程序.Django 的目标是让开发者能够以快速和 ...
- Win10-常用cmd命令与快捷键
以下全部是本人私认为平时经常用到的指令,持续更行中- 常用快捷键 win + R : 输入cmd回车,打开命令提示符界面 win + E : 打开文件资源管理器(俗称:文件夹) win + S : 搜 ...
- Vue3.x+springboot集成pageoffice
说明:由于pageoffice浏览器是ie内核,vue3不兼容ie.所以需要把页面放在后端 一,前端项目: 1.index.html页面引用pageoffice.js <script type= ...
- zabbix使用
安装部署6.0版本 获取仓库 https://www.zabbix.com/cn/download?zabbix=6.0&os_distribution=centos&os_versi ...