Iterators and generators in JavaScript detailed _javascript tips

Source: Internet
Author: User
Tags generator terminates

Working with each item in the collection is a very common operation, and JavaScript provides a number of ways to iterate over a collection, from simple for and for Each loop to map (), filter (), and array comprehensions (arrays of derivations). In JavaScript 1.7, iterators and generators bring new iterative mechanisms in the JavaScript core syntax, and also provide a mechanism for customizing for...in and for Each loop behavior.

Iterators

An iterator is an object that accesses an element of a collection sequence at a time, and tracks the current position of the iteration in the sequence. In JavaScript, an iterator is an object that provides a next () method, and the next () method returns the next element in the sequence. This method throws a Stopiteration exception when all the elements in the sequence are traversed.

Once an iterator object is established, it can be invoked implicitly by an explicit repeat invocation of next (), or by using JavaScript's for...in and for Each loop.

A simple iterator that iterates over objects and arrays can be created using iterator ():

Copy Code code as follows:

var lang = {name: ' JavaScript ', birthyear:1995};
var it = iterator (lang);

Once the initialization is complete, the next () method can be called to access the object's key-value pairs in turn:

Copy Code code as follows:

var pair = It.next (); Key-value pairs are [' name ', ' JavaScript ']
Pair = It.next (); The key value pair is ["Birthday", 1995]
Pair = It.next (); An ' Stopiteration ' exception was thrown

The for...in loop can be used to replace an explicit call to the next () method. When the stopiteration exception is thrown, the loop terminates automatically.

Copy Code code as follows:

var it = iterator (lang);
For (var pair in it)
print (pair); Each output one of the [key, value] key values in it

If you only want to iterate over the key value of an object, you can pass the second argument to the iterator () function, and the value is true:

Copy Code code as follows:

var it = iterator (lang, true);
for (var key in it)
print (key); Output key value at a time

One advantage of accessing objects with iterator () is that custom attributes that are added to Object.prototype are not included in the Sequence object.

Iterator () can also be used on arrays:

Copy Code code as follows:

var langs = [' JavaScript ', ' Python ', ' Haskell '];
var it = iterator (langs);
For (var pair in it)
print (pair); Each iteration output [index, language] key value pair

Just like traversing an object, the result of passing the true as the second parameter in the traversal will be an array index:

Copy Code code as follows:

var langs = [' JavaScript ', ' Python ', ' Haskell '];
var it = iterator (langs, true);
for (var i in it)
print (i); Output 0, then 1, then 2.

Use the Let keyword to assign an index and value to a block variable separately within a loop, and to deconstruct an assignment (destructuring Assignment):

Copy Code code as follows:

var langs = [' JavaScript ', ' Python ', ' Haskell '];
var it = iterators (langs);
for (let [i, Lang] in it)
Print (i + ': ' + lang); Output "0:javascript" and so on

Declaring a custom iterator

Some objects representing the collection of elements should be iterated in a specified way.

1. An object that iterates over an expression range (range) should return one after another to the number contained in the range
2. A leaf node of a tree can be accessed with depth first or breadth first
3. An iteration that represents the results of a database query should be returned in one row, even if the entire result set has not been loaded into a single array
4. An iterator that acts on an infinite mathematical sequence (like the Fibonacci sequence) should return results one after another without creating an infinite length data structure

JavaScript allows you to write code that customizes the logic of the iteration, and it acts on an object

We create a simple Range object that contains a low and a height of two values:

Copy Code code as follows:

function Range (Low, high) {
This.low = low;
This.high = high;
}

Now we create a custom iterator that returns a sequence that contains all the integers in the range. The iterator interface requires that we provide a next () method to return the next element in the sequence or to throw a stopiteration exception.

Copy Code code as follows:

function Rangeiterator (range) {
This.range = range;
This.current = This.range.low;
}
RangeIterator.prototype.next = function () {
if (This.current > This.range.high)
Throw stopiteration;
Else
return this.current++;
};

Our rangeiterator is instantiated with a range instance, while maintaining a current property to track the position of the present sequence.

Finally, to allow Rangeiterator to be combined with range, we need to add a special __iterator__ method for range. When we try to iterate over a Range, it is invoked, and a rangeiterator instance that implements the iterative logic should be returned.

Copy Code code as follows:

range.prototype.__iterator__ = function () {
Return to New Rangeiterator (this);
};

Once we have completed our custom iterator, we can iterate over a scope instance:

Copy Code code as follows:

var range = new Range (3, 5);
for (var i in range)
print (i); Output 3, then 4, then 5.

Builder: A better way to build iterators

Although custom iterators are a useful tool, they need to be carefully planned when they are created, as they require explicit maintenance of their internal state.

The builder provides a powerful feature: it allows you to define a function that contains its own iterative algorithm, and it can automatically maintain its own state.

A builder is a special function that can be used as an iterator factory. If a function contains one or more yield expressions, it is called a builder (note: Node.js also needs to be represented in front of the function name).

Note: Only code blocks in HTML that are contained in <script type= "application/javascript;version=1.7" > (or later) can use the yield keyword. XUL (XML User Interface Language) script tags do not need to specify this particular block of code to access these attributes.

When a generator function is called, the function body does not execute immediately, and it returns a Generator-iterator object. Each time the Generator-iterator next () method is invoked, the function body executes to the next yield expression, and then returns its result. When the function ends or touches the return statement, a Stopiteration exception is thrown.

Use an example to better illustrate:

Copy Code code as follows:

function Simplegenerator () {
Yield "a";
Yield "second";
Yield "third";
for (var i = 0; i < 3; i++)
Yield I;
}

var g = Simplegenerator ();
Print (G.next ()); Output "a"
Print (G.next ()); Output "Second"
Print (G.next ()); Output "Third"
Print (G.next ()); Output 0
Print (G.next ()); Output 1
Print (G.next ()); Output 2
Print (G.next ()); Throw Stopiteration exception

Generator functions can be used directly by a class as a __iterator__ method, and can effectively reduce the amount of code where a custom iterator is needed. We use the builder to rewrite the Range:

Copy Code code as follows:

function Range (Low, high) {
This.low = low;
This.high = high;
}
range.prototype.__iterator__ = function () {
for (var i = This.low i <= this.high; i++)
Yield I;
};
var range = new Range (3, 5);
for (var i in range)
print (i); Output 3, then 4, then 5.

Not all generators will terminate, you can create a generator that represents an infinite sequence. The following generator implements a Fibonacci sequence, where each element is preceded by the preceding two and:

Copy Code code as follows:

Function Fibonacci () {
var fn1 = 1;
var fn2 = 1;
while (1) {
var current = fn2;
FN2 = fn1;
FN1 = fn1 + current;
Yield current;
}
}

var sequence = Fibonacci ();
Print (Sequence.next ()); 1
Print (Sequence.next ()); 1
Print (Sequence.next ()); 2
Print (Sequence.next ()); 3
Print (Sequence.next ()); 5
Print (Sequence.next ()); 8
Print (Sequence.next ()); 13

Generator functions can have parameters, and they will be used the first time the function is called. The generator can be terminated (causing it to throw a Stopiteration exception) by using the return statement. The following Fibonacci () variant has an optional limit parameter that terminates the function when the condition is triggered.

Copy Code code as follows:

function Fibonacci (limit) {
var fn1 = 1;
var fn2 = 1;
while (1) {
var current = fn2;
FN2 = fn1;
FN1 = fn1 + current;
if (limit && current > limit)
Return
Yield current;
}
}

Builder Advanced Features

The generator can compute the yield return value based on requirements, which makes it possible to represent the previously expensive sequence computation requirements, even the infinite sequence shown above.

In addition to the next () method, the Generator-iterator object also has a Send () method that can modify the internal state of the generator. The value passed to send () will be treated as the result of the last yield expression, and the generator will be paused. Before you use the Send () method to pass a specified value, you must call the next () at least once to start the generator.

The following Fibonacci generator uses the Send () method to restart the sequence:

Copy Code code as follows:

Function Fibonacci () {
var fn1 = 1;
var fn2 = 1;
while (1) {
var current = fn2;
FN2 = fn1;
FN1 = fn1 + current;
var reset = yield current;
if (reset) {
FN1 = 1;
FN2 = 1;
}
}
}

var sequence = Fibonacci ();
Print (Sequence.next ()); 1
Print (Sequence.next ()); 1
Print (Sequence.next ()); 2
Print (Sequence.next ()); 3
Print (Sequence.next ()); 5
Print (Sequence.next ()); 8
Print (Sequence.next ()); 13
Print (Sequence.send (true)); 1
Print (Sequence.next ()); 1
Print (Sequence.next ()); 2
Print (Sequence.next ()); 3

Note: The interesting point is that calling send (undefined) is exactly the same as calling next (). However, when the Send () method is invoked to start a new generator, a TypeError exception is thrown except for undefined other values.

You can invoke the throw method and pass an exception value that it should throw to force the generator to throw an exception. This exception is thrown from the current context and pauses the generator, similar to the current yield execution, except for the throw value statement.

If yield is not encountered during the processing of an exception being thrown, the exception is passed until the throw () method is invoked, and subsequent calls to next () cause the stopiteration exception to be thrown.

The builder has a close () method to force the generator to end. Ending a generator can have the following effects:

1. Finally words that are valid in all generators will be executed
2. If the finally clause throws any exception other than Stopiteration, the exception is passed to the caller of the close () method
3. Generator will terminate

Builder expression

An obvious disadvantage of array derivations is that they cause the entire array to be constructed in memory. The cost is negligible when input to the deduced itself is a decimal group-however, the problem may occur when the input array is large or when a new expensive (or infinite) array generator is created.

The generator allows you to compute the sequence latency (lazy computation) and compute the elements on demand as needed. The generator expression is syntactically almost the same as a set of derivations-it replaces the square brackets with parentheses (and replaces the for each...in with for...in)-but it creates a generator instead of an array, which allows the computation to be deferred. You can think of it as a short syntax for creating a builder.

Suppose we have an iterator it to iterate over a huge sequence of integers. We need to create a new iterator to iterate over even. An array derivation will create an entire array in memory that contains all the even numbers:

Copy Code code as follows:

var doubles = [I * 2 for (I in it)];

The builder expression creates a new iterator and calculates the even value on demand as needed:

Copy Code code as follows:

var it2 = (I * 2 for (i in it));
Print (It2.next ()); The first even number inside it
Print (It2.next ()); The second even number inside it

When a generator is used as a function parameter, parentheses are used as function calls, meaning that the outermost parentheses can be omitted:

Copy Code code as follows:

var result = dosomething (i * 2 for (i in it));

End.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.