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
的角度想一下,如今让你去遍历一个数组,你须要知道什么信息呢?异步
带着这样的思考,咱们打印一个数组来看看这里面的玄机:ide
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()
方法:post
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: undefinedui
咱们发现这个 iterator.next()
每次都返回了一个对象。这对象包含两个信息:当前下标的值,以及遍历是否结束的标志。这印证了咱们以前思考,有了这两个信息,你做为 for of
函数,也能打印出数组的每一项了不是吗?this
新的问题来了,iterator
究竟是何方神圣呢?spa
iterator
(迭代器) & The iterator protocol
(迭代协议)聊到了 iterator
咱们不得不先说一下 The iterator protocol
(迭代协议).net
MDN 上是这么说的:The iterator protocol
容许 JavaScript
对象去定义或定制它们的迭代行为 ,因此上面出现的 Symbol.iterator
这个方法,就是数组对于这个协议的实现。那么按照这个协议,数组是怎么实现了一个 iterator
呢?
这一大段看起来比较费劲,简单来讲就像咱们上一章节所印证的,它实现的方式是定义了一个 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
看到这个名字以为很厉害哈,但其实很简单,写一个 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
有什么用? 聪明的同窗可能已经猜到了,是的,它可以中断执行代码的特性,能够帮助咱们来控制异步代码的执行顺序:
例若有两个异步的函数 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
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)
https://www.jianshu.com/p/36c74e4ca9eb