Detailed explanation of the iterator and generator in JavaScript

Source: Internet
Author: User

Detailed explanation of the iterator and generator in JavaScript

Processing each item in a set is a very common operation. JavaScript provides many methods to iterate a set, from simple for and for each loops to map (), filter () and array comprehensions (array derivation ). In JavaScript 1.7, The iterator and generator bring new iteration mechanisms in the core JavaScript syntax, and also provide custom... In and for each.

Iterator

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

Once an iterator object is created, you can call next () explicitly or use JavaScript... In and for each are implicitly called.

You can use Iterator () to create an Iterator that iterates on objects and arrays:

 

The Code is as follows:


Var lang = {name: 'javascript ', birthYear: 1995 };
Var it = Iterator (lang );

 

Once Initialization is complete, the next () method can be called to access the key-value pairs of objects in sequence:

 

The Code is as follows:


Var pair = it. next (); // the key-value pair is ["name", "JavaScript"]
Pair = it. next (); // the key-value pair is ["birthday", 1995]
Pair = it. next (); // A 'stopiteration' exception is thrown.

 

For... An in loop can be used to replace an explicit call to the next () method. When a StopIteration exception is thrown, the loop is automatically terminated.

 

The Code is as follows:


Var it = Iterator (lang );
For (var pair in it)
Print (pair); // each time a [key, value] key-value pair in it is output

 

If you only want to iterate the key value of an object, you can input the second parameter to the Iterator () function. The value is true:

 

The Code is as follows:


Var it = Iterator (lang, true );
For (var key in it)
Print (key); // output the key value each time.

 

One advantage of using Iterator () to access an Object is that the custom attributes added to Object. prototype are not included in the sequence Object.

Iterator () can also be used on Arrays:

 

The Code is as follows:


Var langs = ['javascript ', 'python', 'haskell'];
Var it = Iterator (langs );
For (var pair in it)
Print (pair); // output the [index, language] key-value pair for each iteration

 

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

 

Var langs = ['javascript ', 'python', 'haskell'];
Var it = Iterator (langs, true );
For (var I in it)
Print (I); // output 0, followed by 1, followed by 2

 

You can use the let keyword to assign indexes and values to the block variables respectively within the loop, and Destructuring the values (Assignment ):

 

The Code is as follows:


Var langs = ['javascript ', 'python', 'haskell'];
Var it = Iterators (langs );
For (let [I, lang] in it)
Print (I + ':' + lang); // outputs "0: JavaScript", etc.

 

Declare a custom iterator

Some objects that represent the Element Set should be iterated in a specified way.

1. iteration of an object that represents a Range should return the numbers contained in this Range one by one.
2. The leaf nodes of a tree can be accessed with depth-first or breadth-first.
3. iterate an object that represents the database query results should be returned in one row, even if the entire result set is not fully loaded into a single array
4. The iterator acting on an infinite mathematical sequence (like the Fibonacci sequence) should return results one by one without creating an infinite-length data structure.

JavaScript allows you to write code for custom iteration logic and apply it to an object.

We create a simple Range object with two values: low and high:

 

The Code is as follows:


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

 

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

 

The Code is 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 through a range instance and maintains a current attribute to track the position of the current sequence.

Finally, to enable RangeIterator to combine with Range, we need to add a special _ iterator _ Method for Range. When we try to iterate a Range, it will be called and a RangeIterator instance that implements the iteration logic should be returned.

 

The Code is as follows:


Range. prototype. _ iterator _ = function (){
Return new RangeIterator (this );
};

 

After our custom iterator is complete, we can iterate a range instance:

 

The Code is as follows:


Var range = new Range (3, 5 );
For (var I in range)
Print (I); // output 3, then 4, then 5

 

Generator: a better way to build an iterator

Although custom iterators are useful tools, you need to carefully plan them when creating them because they need to be explicitly maintained.

The generator provides powerful functions: it allows you to define a function that contains your own iteration algorithm, and it can automatically maintain its own State.

A generator 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 generator (Note: Node. js also needs to be represented by * Before the function name ).

Note: The yield keyword can be used only when HTML is contained in <script type = "application/javascript; version = 1.7"> (or later) code blocks. The XUL (XML User Interface Language) script tag can access these features without specifying this special code block.

When a generator function is called, the function body does not execute immediately. It returns a generator-iterator object. Each time you call the next () method of generator-iterator, the function body executes the next yield Expression and returns its results. When a function ends or a return statement is encountered, A StopIteration exception is thrown.

Use an example to better illustrate:

 

The Code is as follows:


Function simpleGenerator (){
Yield "first ";
Yield "second ";
Yield "third ";
For (var I = 0; I <3; I ++)
Yield I;
}

Var g = simpleGenerator ();
Print (g. next (); // output "first"
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 a StopIteration exception.

 

Generator functions can be directly used as the _ iterator _ method of a class, which can effectively reduce the amount of code where a custom iterator is needed. Let's use the generator to override Range:

The Code is 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 are terminated. You can create a generator that represents an infinite sequence. The following generator implements a Fibonacci sequence, that is, each element is the sum of the first two:

The Code is as follows:


Function fibonacci (){
Var fn1 = 1;
Var fn2 = 1;
While (1 ){
Var current = fn2;
Fn2 = fn1;
Fn1 = fn1 + current;
Yield current;
}
}

Var sequence = maid ();
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

 

A generator function can contain parameters that are used when the function is called for the first time. 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, which terminates the function when the condition is triggered.

 

The Code is 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;
}
}

 

Advanced features of Generators

The generator can calculate the yield return value as needed, which can represent the previous expensive sequence computing requirements, or even the infinite sequence shown above.

In addition to the next () method, the generator-iterator object also has a send () method, which 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 suspended. Before you use the send () method to transmit a specified value, you must call next () at least once to start the generator.

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

 

The Code is 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 = maid ();
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 call to send (undefined) is exactly the same as the call to next. However, when the send () method is called to start a new generator, a TypeError exception will be thrown for values other than undefined.

You can call the throw method and pass an abnormal value that it should throw to force the generator to throw an exception. This exception will throw and pause the generator from the current context, similar to the current yield execution, but is replaced with the throw value statement.

If yield is not encountered during the process of throwing an exception, the exception will be passed until the throw () method is called and next () is called, which will cause the StopIteration exception to be thrown.

The generator has a close () method to force the generator to end. Ending a generator will have the following impact:

1. All valid finally words in the generator will be executed
2. If the finally statement throws any exception except StopIteration, the exception will be passed to the caller of the close () method.
3. The generator will terminate

Generator expression

An obvious disadvantage of array derivation is that they will cause the entire array to be constructed in memory. When the input to the derivation is itself a small array, its overhead is negligible-however, when the input array is large or creates a new expensive (or infinite) an error may occur during array generator.

The generator allows lazy computation to compute elements as needed. The generator expression is almost syntactically the same as the array derivation-It replaces square brackets with parentheses (and uses... in instead of for each... in)-but it creates a generator instead of an array, so that computing can be delayed. You can think of it as a short syntax for creating a generator.

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

The Code is as follows:


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

 

The generator expression will create a new iterator and calculate the even value as needed:

The Code is as follows:


Var it2 = (I * 2 for (I in it ));
Print (it2.next (); // The first even number in it
Print (it2.next (); // The second even number in it

 

When a generator is used as a function parameter, parentheses are used as a function call, which means that the outermost parentheses can be omitted:

 

The Code is as follows:


Var result = doSomething (I * 2 for (I in it ));

 

Related Article

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.