ES6 iterators and generators
1. Iterator
JavaScript's original data structures that represent "collections" are mainly arrays and objects. ES6 adds Map and Set. This requires a unified interface mechanism to handle all the different data structures. Iterators are one such mechanism. It is an interface that provides a unified access mechanism for a variety of different data structures. Any data structure can complete the traversal operation (ie, process all members of the data structure in turn) as long as the Iterator interface is deployed .
1. The role of Iterator:
Provide a unified and convenient access interface for various data structures;
Enables members of a data structure to be arranged in a certain order
ES6 created a new traversal command for...of loop, and the Iterator interface is mainly used for for...of consumption.
2. Native data with iterator interface (for of traversal)
- Array
- set container
- map container
- String
- the arguments object of the function
- NodeList object
let arr3 = [1, 2, 'kobe', true];
for(let i of arr3){
console.log(i); // 1 2 kobe true
}
let str = 'abcd';
for(let item of str){
console.log(item); // a b c d
}
function fun() {
for (let i of arguments) {
console.log(i) // 1 4 5
}
}
fun(1, 4, 5)
var engines = new Set(["Gecko", "Trident", "Webkit", "Webkit"]);
for (var e of engines) {
console.log(e);
}
// Gecko
// Trident
// Webkit
3. How iterators work
- Create a pointer object pointing to the start of the data structure.
- The first time the next method is called, the pointer automatically points to the first member of the data structure
- Next, the next method is called continuously, and the pointer will move backward until it points to the last member.
- Each call to the next method returns an object containing value and done, {value: the value of the current member, done: boolean}
- value indicates the value of the current member, and the boolean value corresponding to done indicates whether the traversal of the current data structure is over.
- When the traversal ends, the returned value is undefined, and the done value is true
4. Write an iterator by hand
function myIterator(arr) {
let nextIndex = 0
return {
next: function() {
return nextIndex < arr.length
{ value: arr[nextIndex++], done: false }
: { value: undefined, done: true }
}
}
}
let arr = [1, 4, 'ads']// prepare a data
let iteratorObj = myIterator(arr)
console.log(iteratorObj.next()) // All iterator objects have a next() method, which returns a result object
console.log(iteratorObj.next())
console.log(iteratorObj.next())
console.log(iteratorObj.next())
5. Notes
① for of loop does not support traversal of ordinary objects
var obj = { a: 2, b: 3 }
for (let i of obj) {
console.log(i) // Uncaught TypeError: obj is not iterable
}
The object's Symbol.iterator property, pointing to the object's default iterator method. When using for of to traverse a data structure, first go to Symbol.iterator, and traverse if you find it. If you don't find it, you can't traverse it.Uncaught TypeError: XXX is not iterable
② When using the spread operator (...) or destructuring assignments to arrays and Set structures, the Symbol.iterator method will be called by default
let arr1 = [1,3]
let arr2 = [2,3,4,5]
arr2 = [1,...arr2,6]
console.log(arr2) // [1, 2, 3, 4, 5, 6]
2. Generator
1. Concept
- Generator function is an asynchronous programming solution provided by ES6 , and its syntax and behavior are completely different from traditional functions.
- Syntactically, it can first be understood as the Generator function is a state machine that encapsulates multiple internal states .
- In addition to the state machine, the Generator function is also a traverser object generation function .
- Pauseable functions (lazy evaluation), yield can be paused, next method can be started. Each time it returns the expression result after yield
2. Features
- There is an asterisk between the function keyword and the function name;
- Use yield expressions inside the function body to define different internal states
function* generatorExample(){
console.log("Start execution")
yield 'hello';
yield 'generator';
}
// generatorExample()
// This call method Generator function will not execute
let MG = generatorExample() // return pointer object
MG.next() //Start execution {value: "hello", done: false}
The Generator function is executed in sections. The internal logic of the function calling the next method starts to execute. When the yield expression is encountered, it stops and returns {value: expression result after yield/undefined, done: false/true}
. Calling the next method again will start from the yield at the last stop until the end.
function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}
var hw = helloWorldGenerator();
hw.next()// { value: 'hello', done: false }
hw.next()// { value: 'world', done: false }
hw.next()// { value: 'ending', done: true }
hw.next()// { value: undefined, done: true }
On the first call, the Generator function starts executing until the first yield expression is encountered. The next method returns an object whose value attribute is the value of the current yield expression hello, and the value of the done attribute is false, indicating that the traversal has not ended.
On the second call, the Generator function continues from where the last yield expression left off until the next yield expression. The value attribute of the object returned by the next method is the value world of the current yield expression, and the value of the done attribute is false, indicating that the traversal has not ended.
In the third call, the Generator function executes from the place where the last yield expression stopped until the return statement (if there is no return statement, it executes until the end of the function). The value property of the object returned by the next method is the value of the expression immediately following the return statement (if there is no return statement, the value of the value property is undefined), and the value of the done property is true, indicating that the traversal has ended.
The fourth call, the Generator function has finished running, the value property of the object returned by the next method is undefined, and the done property is true. Calling the next method later will return this value.
3. next pass parameters
The yield expression itself does not return a value, or it always returns undefined. The next method can take one parameter, which will be used as the return value of the previous yield expression.
function* generatorExample () {
console.log('Start execution')
let result = yield 'hello'
console.log(result)
yield 'generator'
}
let MG = generatorExample()
MG.next()
MG.next()
// start execution
// undefined
// {value: "generator", done: false}
When no value is passed, result is undefined by default. Next, we pass a parameter to the second next, and see what the output is?
function* generatorExample () {
console.log('Start execution')
let result = yield 'hello'
console.log(result)
yield 'generator'
}
let MG = generatorExample()
MG.next()
MG.next(11)
// start execution
// 11
// {value: "generator", done: false}
4. Relationship with the Iterator interface
We mentioned above that the object does not have an iterator interface, and an error will be reported when traversing with for...of.
let obj = { username: 'kobe', age: 39 }
for (let i of obj) {
console.log(i) // Uncaught TypeError: obj is not iterable
}
Since the Generator function is the traverser generating function, the Generator can be assigned to the Symbol.iterator property of the object, so that the object has the Iterator interface .
let obj = { username: 'kobe', age: 39 }
obj[Symbol.iterator] = function* myTest() {
yield 1;
yield 2;
yield 3;
};
for (let i of obj) {
console.log(i) // 1 2 3
}
In the above code, the Generator function is assigned to the Symbol.iterator property, so that the obj object has the Iterator interface and can be traversed by for of.
5.Generator's asynchronous application
Business needs:
- Send ajax request to get news content
- After the news content is successfully obtained, send the request again to obtain the corresponding news comment content
- If the news content acquisition fails, there is no need to send the request again.
How to implement (the front-end core code is as follows):
function* sendXml() {
// url is the data passed in by next
let url = yield getNews('http://localhost:3000/news?newsId=2');//Get news content
yield getNews(url);//Get the corresponding news comment content, only by first getting the news data and piecing it together into a url, can you make a request to the background
} }
function getNews(url) {
$.get(url, function (data) {
console.log(data);
let commentsUrl = data.commentsUrl;
let url = 'http://localhost:3000' + commentsUrl;
// When the news content is successfully obtained, send a request to obtain the corresponding comment content
// Call next to pass the meeting as the return value of the yield from the last pause
sx.next(url);
})
}
let sx = sendXml();// Send a request to get news content
sx.next();