从 for of 聊到 Generator
2019独角兽企业重金招聘Python工程师标准>>>
你能学到什么
- 对
for of
更深入的理解 iterator
到底是何方神圣?- 数组也是对象,为什么不能用
for of
来遍历对象呢? - 如何实现对象的
for of
? Generator
又是何方神圣?Generator
有什么用呢?
聊聊for of
说起 for of
相信每个写过 JavaScript
的人都用过 for of
,平时我们用它做什么呢?大多数情况应该就是遍历数组了,当然,更多时候,我们也会用 map()
或者 filer()
来遍历一个数组。 但是就像我们标题里面说的,它跟 Generator
能扯上什么关系呢?
首先我们想一个问题,为什么使用 for of
或者 map()
/ filer()
方法就可以遍历一个数组 (或者类数组对象: Strings
, Maps
, Sets
, arguments
) 呢? 为什么不能用他们来遍历一个对象呢?
在真正揭开谜底之前,站在 for of
的角度想一下,现在让你去遍历一个数组,你需要知道什么信息呢?
- 对应下标的值
- 是否遍历结束的标志
带着这样的思考,我们打印一个数组来看看这里面的玄机:
const numbersArray = [1, 2, 3];console.dir(numbersArray);
复制代码
数组 (或者类数组对象: Strings
, Maps
, Sets
, arguments
) 的原型中都实现了一个方法 Symbol.iterator
,问题来了,那么这个 Symbol.iterator
又有什么用呢? 拿出来试一下就知道了:
let iterator = numbersArray[Symbol.iterator]();
// 我们把这个 Symbol.iterator 打印一下看看里面到底有些什么
console.dir(iterator);
复制代码
这里有一个 next()
方法对吗?执行这个 next()
方法:
iterator.next(); // 输出 {value: 1, done: false}
iterator.next(); // 输出 {value: 2, done: false}
iterator.next(); // 输出 {value: 3, done: false}
iterator.next(); // 输出 {value: undefined, done: true}
复制代码
请注意,当下标超出时,value: undefined
我们发现这个 iterator.next()
每次都返回了一个对象。这对象包含两个信息:当前下标的值,以及遍历是否结束的标志。这印证了我们之前思考,有了这两个信息,你作为 for of
函数,也能打印出数组的每一项了不是吗?
新的问题来了,iterator
到底是何方神圣呢?
iterator
(迭代器) & The iterator protocol
(迭代协议)
聊到了 iterator
我们不得不先说一下 The iterator protocol
(迭代协议)
" The iterable protocol allows JavaScript objects to define or customize their iteration behavior " - MDN
MDN 上是这么说的:The iterator protocol
允许 JavaScript
对象去定义或定制它们的迭代行为 ,所以上面出现的 Symbol.iterator
这个方法,就是数组对于这个协议的实现。那么按照这个协议,数组是怎么实现了一个 iterator
呢?
"In JavaScript an iterator is an object which defines a sequence and potentially a return value upon its termination. More specifically an iterator is any object which implements the Iterator protocol by having a next() method which returns an object with two properties: value, the next value in the sequence; and done, which is true if the last value in the sequence has already been consumed. If value is present alongside done, it is the iterator's return value." - MDN
这一大段看起来比较费劲,简单来说就像我们上一章节所印证的,它实现的方式是定义了一个 next()
方法,而这个 next()
方法每次被执行都会返回一个对象: {value:xxx/undefined , done: true/false }
其中 value
代表的是当前遍历到的值,done
代表是否遍历结束。
本小节回答了我们之前的提问: 为什么不能用 for of
来遍历一个对象呢? 原因很简单:JavaScript
的对象中没有实现一个这样的 iterator
。你可以打印一个对象来看看结果如何:
console.dir({ a: 1, b: 2 });
复制代码
okay, 到这里如果就结束的话,那我们了解得还不够深入,于是再问一个问题:
Why is there no built-in object iteration ? (为什么在 object
中没有内置迭代器呢? )
为什么在 object
中没有内置迭代器呢?
对啊,为什么呢? 我们在各样的场景中也需要来遍历一个对象啊?为什么没有内置一个迭代器呢?要回答这个问题,我们得从另外一个角度出发,了解一些基本的概念:
我们常常说遍历对象,但是简单来说,只会在两种层级上来对一个 JavaScript
对象进行遍历:
程序的层级 - 什么意思呢?在程序层级上,我们对一个对象进行迭代,是在迭代展示其结构的对象属性。 可能还不是很好理解,举个栗子:
Array.prototype.length
这个属性与对象的结构相关,但却不是它的数据。数据的层级 - 意味着迭代数据结构并提取它的数据。举个栗子:我们在迭代一个数组的时候,迭代器是对于它的 每一个数据进行迭代,如果
array = [a, b, c, d]
那么迭代器访问到的是1, 2, 3, 4
。
明白了这个缘由,JavaScript
虽然不支持用 for of
来遍历对象,但是提供了一个 for in
方法来遍历所有非 Symbol
类型并且是可枚举的属性。
标准不支持,如果我们就是要用 for-of
来遍历对象呢?那我们可以任性的实现一个啊:
Object.prototype[Symbol.iterator] = function*() {for (const [key, value] of Object.entries(this)) {yield { key, value };}
};
复制代码
for (const { key, value } of { a: 1, b: 2, c: 3 }) {console.log(key, value);
}
复制代码
不知道你有没有注意一个细节,在我们任性的实现一个 iterator
的代码中,我们用到了一个很奇怪的结构 function*() {}
,这个就是我们接下来要介绍的 Generator
Generators
看到这个名字觉得很厉害哈,但其实很简单,写一个 Generator
你只需要在函数名和 function
关键字中间加一个 *
号就可以了。至于里面的 yield
是什么,后面会说的。
talk is cheap , show me the code
,用一个例子,简单说一下概念。
我们现在定义了一个这样的 Generator
叫做 gen
function* gen() {yield 1;yield 2;yield 3;yield 4;
}
复制代码
我们只能看到,这里面有 4 个语句,那打印一下看看呗:
这里发现了一个熟悉的函数,next()
方法,我们把 gen
实例化一下,执行一下它的 next()
来看看结果:
还是熟悉的味道,那么到这里,我们已经知道,Generator
可以实例化出一个 iterator
,并且这个 yield
语句就是用来中断代码的执行的,也就是说,配合 next()
方法,每次只会执行一个 yield
语句。
多说一句,针对 Generator
本身,还有一个有意思的特性,yield
后面可以跟上另一个 Generator
并且他们会按照次序执行:
function* gen() {yield 1;yield* gen2();return;
}function* gen2() {yield 4;yield 5;
}let iterator = gen();
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
复制代码
结果很有意思不是吗?而且 return
会终结整个 Generator
,换句话说:写在 return
后面的 yield
不会执行。
Generator 有什么用?
Generator
有什么用? 聪明的同学可能已经猜到了,是的,它能够中断执行代码的特性,可以帮助我们来控制异步代码的执行顺序:
例如有两个异步的函数 A
和 B
, 并且 B
的参数是 A
的返回值,也就是说,如果 A
没有执行结束,我们不能执行 B
。
那这时候我们写一段伪代码:
function* effect() {const { param } = yield A();const { result } = yield B(param);console.table(result);
}
复制代码
这时候我们如果需要得到 result
那么我们就需要:
const iterator = effect()
iterator.next()
iterator.next()
复制代码
执行两次 next()
得到结果,看起来很傻不是吗?有没有好的办法呢?(废话,肯定有啊) 假设你在每次执行 A()
/ B()
的请求结束之后,都会自动执行 next()
方法呢?这不就解决了吗?
这样的库早就存在了,建议大家参考 co
的源码,当然你也可以通过阅读 这篇文章 来看看,到底 Generator
是怎么玩的。
ps:
https://blog.csdn.net/zshdd/article/details/82897452
Generator函数自动执行
function readFile(fileName) {
return new Promise(function (resolve,reject) {
fs.readFile(fileName,function (err,data) {
if(err) reject(err);
resolve(data.toString());
})
})
}
function* gentT2() {
var f1 = yield readFile('ip.txt');
var f2 = yield readFile('ip1.txt');
console.log(f1,f2);
}
function run(gen) {
var g=gen();
function next(data) {
var res=g.next(data);
if(res.done) return res.value;
res.value.then(function (data) {
next(data);
});
}
next();
}
run(gentT2);
// 方便的自动执行generator函数 类似的库(co)
深入理解js中的yield
https://www.jianshu.com/p/36c74e4ca9eb
- yield是ES6的新关键字,使生成器函数执行暂停,yield关键字后面的表达式的值返回给生成器的调用者。它可以被认为是一个基于生成器的版本的return关键字。
- yield关键字实际返回一个IteratorResult(迭代器)对象,它有两个属性,value和done,分别代表返回值和是否完成。
- yield无法单独工作,需要配合generator(生成器)的其他函数,如next,懒汉式操作,展现强大的主动控制特性。
转载于:https://my.oschina.net/jack088/blog/3003009
从 for of 聊到 Generator相关推荐
- python之路day14--列表生成式、生成器generator、生成器并行
列表生成式 列表生成式阅读量: 44 现在有个需求,现有列表a=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],要求你把列表里的每个值加1,你怎么实现?你可能会想到2种方式 二逼青年版 ...
- Generator 实现
Generator 实现 Generator 是 ES6 中新增的语法,和 Promise ⼀样,都可以⽤来异步编程 // 使⽤ * 表示这是⼀个 Generator 函数 // 内部可以通过 yie ...
- IM后台开发六之群聊接口实现
单聊做完了,接下来就是群聊的实现了. 在做群聊之前,我们还是要先分析一下实现群聊应该需要实现哪些接口. 群聊接口分析 1.创建群聊 首先我们第一个要实现的接口,就是创建群聊了.毕竟想要有群,必须创建群 ...
- PyQt5+ Python3 模拟QQ群聊
尊重原创,原文链接 关于PyQt5+Python3开发环境安装可以参考上篇.这里实现的是一种群聊工具,socket类使用的是Qt的TcpSocket, TcpServer 类:线程类使用的QTread ...
- Python错误:AttributeError: 'generator' object has no attribute 'next'解决办法
今天在学习生成器对象(generation object)运行以下代码时,遇到了一个错误: #定义生成器函数 def liebiao(): for x in range(10): yield x #函 ...
- 3d stroke插件下载_推荐一款好用的PS 3D地图插件,PS插件3D Map Generator ,一键生成地图神器...
今天,给大家推荐一个强大的Photoshop(PS)地图插件--3D Map Generator,可以方便制作3D特效效果. 3D Map Generator插件是什么 3D Map Generato ...
- c++数据结构队列栈尸体_一本正经的聊数据结构(3):栈和队列
前文传送门: 「一本正经的聊数据结构(1):时间复杂度」 「一本正经的聊数据结构(2):数组与向量」 引言 前一篇内容我们介绍了数组和向量,虽然说向量是数组的一个升级版,但是在另一个维度上,他们都属于 ...
- 利用ZYNQ SOC快速打开算法验证通路(5)——system generator算法IP导入IP integrator
一.前言 利用FPGA设计算法一直以来都是热点,同样也是难点.将复杂的数学公式 模型通过硬件系统来搭建,在低延时 高并行性等优势背后极大提高了设计难度和开发周期.Xilinx公司的sysGen(sys ...
- Jfinal Generator 不需要生成带某个前缀的表名数组的方法
2019独角兽企业重金招聘Python工程师标准>>> package com.demo.common.model; import javax.sql.DataSource; imp ...
最新文章
- sar sensor传感器的作用_传感器攻防战-惯导IMU
- 2W+好评,这个python数据分析课程免费开放3天!
- 都 2020 年了,这些 OKR 的坑你还要踩吗?
- 互联网性能与容量评估的方法论和典型案例
- docker piwik
- docker快速入门01——docker安装与简单应用
- springboot集成mybati 后又使用mybatisPlus 出现的问题 BindingException:Invalid bound statement
- 【数字全排列】LeetCode 60. Permutation Sequence
- 小型数控雕刻机制作Arduino_【图片】广州玉邦雕刻机厂家【玉石雕刻机吧】
- C++ 一个简单的log类的实现
- vs2019社区版+qt5.14.2+Coin3D安装
- 集成电路哪家强?现在就为你透彻分析!
- 说课c语言字符数组,C语言说课(超好).ppt
- system libzip must be upgraded to version >= 0.11
- 如何用Python写一个小游戏(2)
- 瀚高DB兼容MySQL if函数
- python小应用之整理手机图片_手把手:扫描图片又大又不清晰?这个Python小程序帮你搞定!...
- 【JAVA基础】java基础之-泛型详解
- 0.96寸OLED显示屏标准库移植HAL库(模拟IIC) - 基于STM32
- 安卓沉浸式状态栏_《宫廷秘传》今日安卓计费删档开测
热门文章
- ROS-3 : Catkin工作空间和ROS功能包
- EventBus VS Spring Event
- jmeter--轻量级接口自动化测试框架
- 全球最性感的13大仿真机器人 功能太全难把持!
- spring框架学习(一)入门
- MySQL的binlog日志
- linux下svn重新定位的方法
- 【Linux 内核 内存管理】优化内存屏障 ① ( barrier 优化屏障 | 编译器优化 | CPU 执行优化 | 优化屏障源码 barrier 宏 )
- 【Android 安装包优化】开启资源压缩 ( 资源压缩配置 | 启用严格模式的资源引用检查 | 自定义保留/移除资源配置 | 资源压缩效果 )
- 【计算理论】可判定性 ( 对角线方法 | 证明自然数集 N 与实数集 R 不存在一一对应关系 )