Iterators and Generators
Iterators have been used in many programming languages as a way to more easily work with collections of data. In ECMAScript 6, JavaScript adds iterators as an important feature of the language. When coupled with new array methods and new types of collections (such as sets and maps), iterators become even more important for efficient processing of data.
在其他的语言中,遍历器被用来更加方便的处理数据集合。在ES6中,js增加了遍历器作为这个语言的重要特征。当结合新的数组方法和新的集合类型(例如set和map),因为高效的处理数据的能力,遍历器变得更加重要。
What are Iterators?-什么是遍历器
Iterators are nothing more than objects with a certain interface. That interface consists of a method called next()
that returns a result object. The result object has two properties, value
, which is the next value, and done
, which is a boolean value that’s true
when there are no more values to return. The iterator keeps an internal pointer to a location within a collection of values and, with each call to next()
, returns the next appropriate value.
遍历器只是一个拥有特定接口的对象。这个接口包含了一个方法叫做next()可以返回结果对象。结果对象有两个属性:value-next的值,和done-布尔值,为true的时候表示下一次将没有返回值。遍历器有一个内部指针来定位集合的值,每次调用next方法,都返回下一个适当的值。
If you call next()
after the last value has been returned, the method returns done
as true
and value
contains the return value for the iterator. The return value is not considered part of the data set, but rather a final piece of related data or undefined
if no such data exists. (This concept will become clearer in the generators section later in this chapter.)
如果在返回最后一个值之后又调用了next(),这个方法返回的done的值为true,value包含了遍历器的返回值,返回值不是集合的一部分,而是最后一个相关的数据或者undefined如果没定义这个数据。
With that understanding, it’s fairly easy to create an iterator using ECMAScript 5, for example:
理解到这,使用ES5创建一个遍历器也很简单,例如:
function createIterator(items) { var i = 0; return { next: function() { var done = (i >= items.length); var value = !done ? items[i++] : undefined; return { done: done, value: value }; } }; } var iterator = createIterator([1, 2, 3]); console.log(iterator.next()); // "{ value: 1, done: false }" console.log(iterator.next()); // "{ value: 2, done: false }" console.log(iterator.next()); // "{ value: 3, done: false }" console.log(iterator.next()); // "{ value: undefined, done: true }" // for all further calls console.log(iterator.next()); // "{ value: undefined, done: true }"
The createIterator()
function in this example returns an object with a next()
method. Each time the method is called, the next value in the items
array is returned as value
. When i
is 4, items[i++]
returns undefined
and done
is true
, which fulfills the special last case for iterators in ECMAScript 6.
本例中的createIterator函数返回了一个拥有next方法的对象。每当这个方法调用的时候,items数组中的下一个值作为value属性返回。当i等于4的时候,item[i++]返回undefined和done的值为true,符合ES6中的遍历器特性。
ECMAScript 6 makes use of iterators in a number of places to make dealing with collections of data easier, so having a good basic understanding allows you to better understand the language as a whole.
ES6中在很多地方使用遍历器来处理集合数据更加方便,所以拥有良好的理解可以让你更好的理解整个语言。
Generators-生成器
You might be thinking that iterators sound interesting but they look like a bunch of work. Indeed, writing iterators so that they adhere to the correct behavior is a bit difficult, which is why ECMAScript 6 provides generators. A generator is a special kind of function that returns an iterator. Generator functions are indicated by inserting a star character (*
) after the function
keyword (it doesn’t matter if the star is directly next to function
or if there’s some whitespace between them). The yield
keyword is used inside of generators to specify the values that the iterator should return when next()
is called. So if you want to return three different values for each successive call to next()
, you can do so as follows:
你可能在想,遍历器听起来很有意思,但是它们需要很多的工作。确实,编写一个确保行为正确的遍历器有点困难,这就是为什么ES6引入了generators(生成器),一个generators是一个能够返回遍历器的特殊函数。生成器在function关键字之后增加一个星字符(*)来表示(星字符与function关键字之间是否又空格没有影响)。yield关键字用在生成器内部来制定当遍历器的next方法调用的时候应该返回什么值。所以如果你想每次调用next的时候返回三个不同的值,你可以像下面这样做:
// generator function *createIterator() { yield 1; yield 2; yield 3; } // generators are called like regular functions but return an iterator let iterator = createIterator(); for (let i of iterator) { console.log(i); }
This code outputs the following:
这段代码的输出如下:
1 2 3
In this example, the createIterator()
function is a generator (as indicated by the *
before the name) and it’s called like any other function. The value returned is an object that adheres to the iterator pattern. Multiple yield
statements inside the generator indicate the progression of values that should be returned when next()
is called on the iterator. First, next()
should return 1
, then 2
, and then 3
before the iterator is finished.
在这个例子中,createIterator函数是一个生成器(函数名字之前有星字符),它的调用和其他函数一样。返回的值是一个遍历器形式的对象。生成器内部的多个yield声明表示当调用遍历器的next方法的时候这些值会依次被返回。第一次next返回1,然后是2和3.
Perhaps the most interesting aspect of generator functions is that they stop execution after each yield
statement, so yield 1
executes and then the function doesn’t execute anything else until the iterator’s next()
method is called. At that point, execution resumes with the next statement after yield 1
, which in this case is yield 2
. This ability to stop execution in the middle of a function is extremely powerful and lends to some interesting uses of generator functions (discussed later in this chapter).
也许生成器最有趣的一方面在于每次yield声明执行之后,生成器函数就会停止执行,所以yield 1 执行了知道遍历器的next方法被调用之前这个函数都不会继续往下执行。对于这一点,在yeild声明之后恢复执行,本例中也就是yield2.在函数内部停止执行的能力很强大,这导致了生成器函数的很多有意思的用途。(稍后讨论)
The yield
keyword can be used with any value or expression, so you can do interesting things like use yield
inside of a for
loop:
yield关键字可以和任何的值和表达式一起使用,所以在for循环中你可以使用yield来做一个有趣的事:
function *createIterator(items) { for (let i=0; i < items.length; i++) { yield items[i]; } } let iterator = createIterator([1, 2, 3]); console.log(iterator.next()); // "{ value: 1, done: false }" console.log(iterator.next()); // "{ value: 2, done: false }" console.log(iterator.next()); // "{ value: 3, done: false }" console.log(iterator.next()); // "{ value: undefined, done: true }" // for all further calls console.log(iterator.next()); // "{ value: undefined, done: true }"
In this example, an array is used in a for
loop, yielding each item as the loop progresses. Each time yield
is encountered, the loop stops, and each time next()
is called on iterator
, the loop picks back up where it left off.
在这个例子中,循环一个数组,每次循环都输出数组的一项。每次遇到yield,循环停止,每次调用遍历器的next方法,循环都会从之前停止的地方开始。
Generator functions are an important part of ECMAScript 6, and since they are just functions, they can be used in all the same places.
生成器是ES6 重要的一部分,因为他们只是函数,所以他们和函数的用法一样。
Generator Function Expressions-生成器函数表达式
未完待续。。。。(翻译也是一个挺累的活计)