[前端怪谈_1] 从 for of 聊到 Generator
栏目: JavaScript · 发布时间: 5年前
内容简介:说起首先我们想一个问题,为什么使用在真正揭开谜底之前,站在
说起 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
(迭代协议)
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
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().next() 复制代码
执行两次 next()
得到结果,看起来很傻不是吗?有没有好的办法呢?(废话,肯定有啊) 假设你在每次执行 A()
/ B()
的请求结束之后,都会自动执行 next()
方法呢?这不就解决了吗?
这样的库早就存在了,建议大家参考 co
的源码,当然你也可以通过阅读这篇文章 来看看,到底 Generator
是怎么玩的。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- [前端怪谈_2]从 Dva 的 Effect 到 Generator + Promise 实现异步编程
- 前端科普系列(三):CommonJS 不是前端却革命了前端
- 前端科普系列(三):CommonJS 不是前端却革命了前端
- 前端技术演进(三):前端安全
- 【前端优化】前端常见性能优化
- 【前端学习笔记】前端安全详解
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Numerical Recipes 3rd Edition
William H. Press、Saul A. Teukolsky、William T. Vetterling、Brian P. Flannery / Cambridge University Press / 2007-9-6 / GBP 64.99
Do you want easy access to the latest methods in scientific computing? This greatly expanded third edition of Numerical Recipes has it, with wider coverage than ever before, many new, expanded and upd......一起来看看 《Numerical Recipes 3rd Edition》 这本书的介绍吧!