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中是不允许直接访问内存中的位置; 函数的参数 ...
随机推荐
- Serverless 在阿里云函数计算中的实践
简介: 近日,阿里云 aPaaS&Serverless 前端技术专家袁坤在 CSDN 云原生 meetup 长沙站分享了 Serverless 在阿里云函数计算 FC 的实践. 作者:CSDN ...
- Delta Lake基础介绍(商业版)
简介:介绍 Lakehouse 搜索引擎的设计思想,探讨其如何使用缓存,辅助数据结构,存储格式,动态文件剪枝,以及 vectorized execution 达到优越的处理性能. 作者:李洁杏,Da ...
- 全链路灰度之 RocketMQ 灰度
简介:本文将以上次介绍过的<如何用 20 分钟就能获得同款企业级全链路灰度能力?>中的场景为基础,来进一步介绍消息场景的全链路灰度. 作者:亦盏 之前的系列文章中,我们已经通过全链路金丝 ...
- 汽车之家基于 Flink 的数据传输平台的设计与实践
简介: 数据接入与传输作为打通数据系统与业务系统的一道桥梁,是数据系统与架构中不可或缺的一个重要部分.数据传输系统稳定性和准确性,直接影响整个数据系统服务的 SLA 和质量.此外如何提升系统的易用性, ...
- 技术干货| 阿里云基于Hudi构建Lakehouse实践探索「内附干货PPT下载渠道」
简介: 阿里云高级技术专家王烨(萌豆)在Apache Hudi 与 Apache Pulsar 联合 Meetup 杭州站上的演讲整理稿件,本议题介绍了阿里云如何使用 Hudi 和 OSS 对象存储 ...
- ElasticSearch IK 分词器快速上手
简介: ElasticSearch IK 分词器快速上手 一.安装 IK 分词器 1.分配伪终端 我的 ElasticSearch 是使用 Docker 安装的,所以先给容器分配一个伪终端.之后就可 ...
- IDA动态调试快捷键
1. F2下断点2. F7进入函数,F8单步调试,F9跳到下一个断点,F2下断点,G调到函数地址3. N重名4. g跳到地址和函数名5. u取消把函数汇编变成机器码6. c就是把机器码变成汇编7. F ...
- SemanticFunction 融合 LLM 和传统编程
本文将继续和大家介绍 SemanticKernel 神奇的魔法,将使用 LLM 大语言模型编写的自然语言函数和传统的编程语言编写的函数融合到一起的例子.通过本文的例子,大家可以看到 SemanticK ...
- LabView之MQTT协议使用
一.MQTT概述 MQTT协议是一种消息列队传输协议,采用订阅.发布机制,订阅者只接收自己已经订阅的数据,非订阅数据则不接收,既保证了必要的数据的交换,又避免了无效数据造成的储存与处理.因此在工业物联 ...
- 免费的visual studio智能代码插件——CodeGeeX
CodeGeeX是什么?什么是CodeGeeX? CodeGeeX是一款基于大模型的智能编程助手,它可以实现代码的生成与补全,自动为代码添加注释,不同编程语言的代码间实现互译,针对技术和代码问题的智能 ...