Lazy evaluation, every functional programmer’s wet dream. Soon enough, we will look at generators. But first, let's get a grasp of what iterators and iterables are because to me, they’re a prerequisite for generators.
懒惰的评估,每个函数式程序员的梦想。 很快,我们将研究发电机。 但是首先,让我们了解什么是迭代器和可迭代对象,因为对我而言,它们是生成器的先决条件。
懒惰评估 (Lazy evaluation)
Lazy evaluation means to delay the evaluation of an expression until it’s needed. Lazy evaluation is sometimes referred to as call-by-need.
惰性求值意味着将对表达式的求值延迟到需要时才进行。 惰性评估有时称为按需调用 。
The opposite of lazy evaluation is an eager evaluation. It’s an evaluation strategy used in most programming languages.
懒惰评估的反面是渴望评估 。 这是大多数编程语言中使用的评估策略。
Lazy evaluation makes it possible to:
懒惰评估可以:
- define potentially infinite data structures 定义潜在的无限数据结构
- increase performance by avoiding needless computations 通过避免不必要的计算来提高性能
- customize iteration behavior for data structures that want its elements accessible to the public 为希望其元素可公开访问的数据结构定制迭代行为
迭代器 (Iterators)
Iterators in JavaScript (since ECMAScript 6) are what make it possible to lazy evaluate and create user-defined data sequences.
JavaScript中的迭代器 (自ECMAScript 6起 )使懒惰求值和创建用户定义的数据序列成为可能。
Iteration is a mechanism for traversing data. Iterators are pointers for traversing elements of data structure, called Iterable. A pointer for producing a sequence of values.
迭代是一种遍历数据的机制。 迭代器是遍历数据结构元素(称为Iterable)的指针。 用于产生值序列的指针。
An iterator is an object that can be iterated over. It abstracts a container of data to make it behave like an iterable object.
迭代器是可以迭代的对象。 它抽象出一个数据容器,使其表现得像一个可迭代的对象。
The iterator does not compute the value of each item when instantiated. The next value is generated only when requested. This is useful, especially for large data sets or sequences of an infinite number of elements.
迭代器在实例化时不计算每个项目的值。 仅在请求时才生成下一个值。 这非常有用,特别是对于大型数据集或无限个元素的序列。
可迭代 (Iterables)
Iterables are data structures that want their elements accessible to the public.
可迭代对象是希望其元素可被公众访问的数据结构。
Many APIs accept iterables, for example:
许多API都接受可迭代,例如:
new Map([iterable])
new Map([iterable])
new WeakMap([iterable])
new WeakMap([iterable])
new Set([iterable])
new Set([iterable])
new WeakSet([iterable])
new WeakSet([iterable])
Promise.all([iterable])
Promise.all([iterable])
Promise.race([iterable])
Promise.race([iterable])
Array.from([iterable])
Array.from([iterable])
There’re also statements and expressions that expect iterables, for example:
还有一些期望可迭代的语句和表达式,例如:
for ... of
(loop)for ... of
(循环)...
(spread operator)...
(点差运算符)const [a, b, ..] = iterable;
(destructuring assignment)const [a, b, ..] = iterable;
(销毁工作)yield*
(generator delegation)yield*
(发电机委托)
There’s a number of already built-in iterables in JavaScript:String
, Array
, TypedArray
, Map
, Set
.
JavaScript中已经有许多内置的可迭代对象: String
, Array
, TypedArray
, Map
, Set
。
迭代协议 (Iteration protocols)
Iterators and iterables conform to iteration protocols.
迭代器和可迭代对象符合迭代 协议 。
Protocol is set of interfaces and rules how to use them.
协议是一组接口,并规定了如何使用它们。
Iterators conform to the iterator protocol. Iterables conform to the iterable protocol.
迭代器符合迭代器协议 。 可迭代对象符合可迭代协议 。
可迭代的协议 (The iterable protocol)
Iterable protocol allows JavaScript objects to define or customize their iteration behavior.
可迭代协议允许JavaScript对象定义或自定义其迭代行为。
For an object to become iterable, it must implement an iterator method accessible through Symbol.iterator
. This method is a factory for iterators.
为了使对象变得可迭代,它必须实现可通过Symbol.iterator
访问的迭代器方法。 此方法是迭代器的工厂。
Using TypeScript the iterable protocol looks as follows:
使用TypeScript,可迭代协议如下所示:
interface Iterable {
[Symbol.iterator]() : Iterator;
}
[Symbol.iterator]()
is a zero-argument function. It is invoked on the iterable object, which means you can access the iterable through this
. It can be either a regular function or a generator function.
[Symbol.iterator]()
是零参数函数。 在iterable对象上调用它,这意味着您可以通过this
来访问iterable。 它可以是常规函数或生成器函数。
迭代器协议 (The iterator protocol)
Iterator protocol defines a standard way to produce a sequence of values.
迭代器协议定义了产生值序列的标准方法。
For an object to become an iterator, it must implement a next()
method. Optionally the iterator can implement a return()
method, we will discuss this later in this article.
为了使对象成为迭代器,它必须实现next()
方法。 可选地,迭代器可以实现return()
方法,我们将在本文后面进行讨论。
Using TypeScript the iteration protocol looks as follows:
使用TypeScript,迭代协议如下所示:
interface Iterator {
next() : IteratorResult;
return?(value?: any): IteratorResult;
}
Where IteratorResult
is:
其中IteratorResult
为:
interface IteratorResult {
value?: any;
done: boolean;
}
done
informs the consumer if the iterator has been consumed,false
means there are still values to be produced,true
means the iterator has reached its enddone
告知消费者如果迭代器已被消耗,false
手段仍有值待产,true
手段迭代器已经到达其终点value
can be any JavaScript value, it’s the value exposed to the consumervalue
可以是任何JavaScript值,它是暴露给使用者的值
When done
is true value
can be omitted.
当done
为true时,可以省略value
。
放在一起 (Put together)
To visualize the connection between iterable and iterator see the following image.
要可视化iterable和iterator之间的连接,请参见下图。
![Iterable and iterator](https://i-blog.csdnimg.cn/blog_migrate/f523a69da1d1938c2bb488d266d76379.png)
Show️Showtime (⌨️ Showtime)
Alright, enough theory. Let’s work through some examples. We will start with some basic examples and little by little add what we have learned so far to make things more interesting.
好了,足够的理论。 让我们来看一些例子。 我们将从一些基本示例开始,并逐步添加到目前为止所学的知识,以使事情变得更加有趣。
范围迭代器 (Range iterator)
Let’s begin with a very basic iterator, the createRangeIterator
iterator.
让我们从一个非常基本的迭代器开始,即createRangeIterator
迭代器。
We’re manually calling it.next()
to get the next IteratorResult
. Last call returns { done: true }
which means the iterator is now consumed and will not produce any more values.
我们正在手动调用it.next()
以获得下一个IteratorResult
。 上次调用返回{ done: true }
,这意味着迭代器现在已消耗,将不再产生任何值。
迭代范围迭代器 (Iterable range iterator)
Earlier in this article, I have mentioned that some statements and expressions in JavaScript expect iterables. Because of this, our previous example will not work when used, for instance, with for ... of
loop.
在本文的前面,我已经提到JavaScript中的某些语句和表达式期望可迭代。 因此,我们前面的示例在与for ... of
循环一起使用时将不起作用。
But it’s easy enough to create an object that conforms to both iterator and iterable protocols.
但是创建符合迭代器和可迭代协议的对象非常容易。
To visualize this see the following image.
要使其可视化,请参见下图。
![Iterable iterator](https://i-blog.csdnimg.cn/blog_migrate/2213ac9182b304de66d9ddbf2f97da4f.png)
无限序列迭代器 (Infinite sequence iterator)
Iterators can express sequences of unlimited size because they compute the value only when you ask for it.
迭代器可以表达无限制大小的序列,因为它们仅在您需要时才计算值。
Be careful not to use spread operator (...
) on infinite iterators. JavaScript will try to consume the iterator and since the iterator is infinite it will never reach its end. Instead, your app will crash because you will run out of memory.
注意不要在无限迭代器上使用扩展运算符( ...
)。 JavaScript将尝试使用迭代器,并且由于迭代器是无限的,因此它将永远不会结束。 相反,您的应用程序将崩溃,因为您将耗尽内存。
Also the for ... of
loop over such iterable would be endless. Make sure to exit the loop. Otherwise, you will also run out of memory.
同样for ... of
循环的for ... of
循环将是无止境的。 确保退出循环。 否则,您还将耗尽内存。
关闭迭代器 (Closing iterator)
Earlier we mentioned iterators can optionally have a return()
method. This method is used when the iterator was not iterated over until the end and lets the iterator do a cleanup.
前面我们提到过,迭代器可以有选择地使用return()
方法。 当迭代器直到最后都没有迭代时使用此方法,并让迭代器进行清理。
for ... of
loops can terminate the iteration earlier by:
for ... of
循环可以通过以下方式更早地终止迭代:
break
break
continue
(when you continue outer loop with label)continue
(当您继续使用标签进行外循环时)throw
throw
return
return
The following constructs close iterators that are not consumed:
以下构造关闭了不使用的迭代器:
for ... of
for ... of
yield*
yield*
- destructuring 解构
Array.from
Array.from
Map(), Set(), WeakMap(), WeakSet()
Map(), Set(), WeakMap(), WeakSet()
Promise.all(), Promise.race()
Promise.all(), Promise.race()
Taken from https://2ality.com/2015/02/es6-iteration.html.
If you know that the iterator has reached its end you call the
cleanup()
function manually.如果您知道迭代器已经结束,则可以手动调用
cleanup()
函数。If there was an abrupt completion, the
return()
comes into play and does the cleanup for us.如果突然完成,则
return()
起作用并为我们进行清理。
💥额外 (💥 Extra)
If you have made it to this point, let’s add something extra.
如果您已经做到了这一点,让我们添加一些额外的内容。
组合器 (Combinators)
Combinators are functions that combine existing iterables to create new ones. A composition of iterables.
组合器是将现有可迭代对象组合在一起以创建新可迭代对象的函数。 可迭代的组成。
Because of this, we are able to create a lot of utility functions. How about map
or filter
? See the following code and give it a minute to sink in.
因此,我们能够创建许多实用程序功能。 map
或filter
怎么样? 请参阅下面的代码,花一分钟时间沉浸其中。
Yay! That was a lot of code. Soon we will look at how all this can be refactored using generators and functional programming concepts. You will be surprised how compact all of this code can get.
好极了! 那是很多代码。 很快,我们将研究如何使用生成器和函数式编程概念来重构所有这些。 您将惊讶于所有这些代码的紧凑程度。
Stay safe, stay tuned, and watch out for my upcoming articles, we still have got a lot to cover.
保持安全,保持关注,并注意我的后续文章,我们仍然有很多内容要讲。
资源资源 (Resources)
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Iterators_and_Generators
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Iteration_protocols
https://swizec.com/blog/finally-a-practical-use-case-for-javascript-generators/
https://swizec.com/blog/finally-a-practical-use-case-for-javascript-generators/
https://www.freecodecamp.org/news/how-and-why-you-should-use-python-generators-f6fb56650888/
https://www.freecodecamp.org/news/how-and-why-you-should-use-python-generators-f6fb56650888/
翻译自: https://medium.com/swlh/javascript-lazy-evaluation-iterables-iterators-e0770a5de96f