preface
This article focuses on an in-depth look at ES6 Iterator. Before we get to it, we need to know why we need Iterator in the first place. What’s the reason for it?
Start with loops
In development, we often use loops, for example, to loop through an array:
/ / a for loop
var arr = [1.2.3.4];
for(let i = 0; i < arr.length; i++) {
console.log(arr[i]); // 1, 2, 3, 4
}
// forEach loops can also be used
arr.forEach(item= > {
console.log(item); // 1, 2, 3, 4
})
// You can also use the for in loop
for(let i in arr) {
console.log(arr[i]); // 1, 2, 3, 4
}
Copy the code
If it is looping over each character of the string
/ / a for loop
var str = 'abcde';
for(var i = 0; i < str.length; i++) {
console.log(str[i]); // a b c d e
}
// for in loop
for(var k in str){
console.log(str[k]); // a b c d e
}
// forEach cannot iterate over a string. If it must be used, the string must be converted to an array and then looped through an array
Copy the code
If it’s a loop that outputs a map, it’s ok to have a foreach loop, a regular for loop, and a for… In all not line
var map = new Map(a); map.set('first'.'the first');
map.set('second'.'2');
map.set('third'.'3');
/ / the forEach loop
map.forEach((val,key) = > {
console.log(val, key);
/ / the first first
/ / the next second
/ / the third third
});
Copy the code
For each set of data above, we find that none of the loop methods can be solved at once.
While forEach loops can’t loop strings, strings can be converted to arrays and then printed using forEach, but this is uncomfortable and requires conversion every time you use it. And forEach loops have a disadvantage: you can’t break out of the loop with a break, continue statement, or return from the function body.
For loops, on the other hand, can be complicated in some cases by writing code and not looping objects.
By contrast, for… The disadvantage of IN is that it not only iterates over numeric key names, but also over manually added custom keys, and even keys on the prototype chain. for… In is designed primarily for traversing objects and does not work well for traversing groups of numbers.
The following code
Array.prototype.protoValue = 'hello';
var arr = [1.2.3.4];
arr.test = 'test';
for(let i in arr) {
console.log(arr[i]); // 1 2 3 4 test hello
}
Copy the code
Then again, is there a better cycle that can integrate the above cycle problems? ES6 is for… Of circulation
for… Of cycle occurs
What problem does it solve? First of all, of course, it can solve our problems mentioned above
Loop through the above data
for(let v of arr) {
console.log(v); // 1, 2, 3, 4
}
for(let v of str) {
console.log(v); // a b c d e
}
for(let v of map) {
console.log(v);
// (2) ["first", "first"]
// (2) ["second", "second"]
// (2) ["third", "third"]
}
Copy the code
Check out the pros:
- Concise and direct traversal syntax for groups of numbers
- It avoids all the drawbacks of a for-in loop
- Unlike forEach loops, it can use break, continue, and return statements
Ruan Yifeng’s Introduction to ECMAScript 6 says:
ES6 introduced for… The of loop, as a unified way to traverse all data structures.
Iterator is an interface that provides a unified access mechanism for various data structures. The Iterator interface can be deployed on any data structure to complete traversal (that is, processing all members of the data structure in turn).
A data structure with an iterator interface can use for… The of loop iterates through its members.
That is, not all objects can use for… Only objects that implement the Iterator interface can be used for… Of to iterate over the values.
So what is Iterator? Let’s move on
The Iterator Iterator
concept
It is an interface that provides a unified access mechanism for various data structures. The Iterator interface can be deployed on any data structure to complete traversal (that is, processing all members of the data structure in turn).
role
- To provide a unified and simple access interface for various data structures;
- To enable the members of a data structure to be arranged in some order;
- Create a new traversal command for… The of loop is an Iterator interface for… Of consumption.
Now, Iterator was created primarily to use for… Of methods. But the concrete Iterator concept is a bit abstract, if you want to describe it directly:
Iterator is simply an object with a next() method, and each call to next() returns a result object with two properties, as follows
{value: indicates the current value,done: indicates whether the traversal is complete}Copy the code
Here are some more concepts, let’s comb through them:
Iterator is a special object:
- It has the next() method, which returns a result object when called
- The resulting object has two property values:
value
anddone
. value
Represents a specific return value; Done is a Boolean that indicates whether the collection has completed traversal, true if not, and false otherwise- There is an internal pointer to the starting position of the data structure. Each time the next() method is called, the pointer moves back one position until it points to the last position.
Simulation of the Iterator
Based on the above description, we simulate an iterator as follows
function createIterator(items) {
var i = 0;
return {
next: function() {
var done = (i >= item.length);
varvalue = ! done ? items[i++] :undefined;
return {
done: done,
value: value }; }}; }// Iterator is an iterator object
var iterator = createIterator([1.2.3]);
console.log(iterator.next()); // { done: false, value: 1 }
console.log(iterator.next()); // { done: false, value: 2 }
console.log(iterator.next()); // { done: false, value: 3 }
console.log(iterator.next()); // { done: true, value: undefined }
console.log(iterator.next()); // { done: true, value: undefined }
console.log(iterator.next()); // { done: true, value: undefined }
Copy the code
Process:
- Creates a pointer object that points to the start of the current data structure. That is, the traverser object is essentially a pointer object.
- The next method of the pointer object is called for the first time, and the next method holds the pointer internally through a closure
i
The value of each calli
will+ 1
Point to the next one. So the first time the pointer points to the first member of the data structure, it prints 1. - A second call to the next method of the pointer object, which points to the second member of the data structure, outputs 2.
- The third call to the next method of the pointer object, with the array length 3, outputs 3 at the end of the data structure.
- The fourth call to the next method of the pointer object, at which point the traversal is complete, and the output done is true to indicate completion, and value is undefined. The NTH call after that is the same result.
Iterator: Iterator: Iterator: Iterator: Iterator: Iterator: Iterator Of consumption. We try using for… The Iterator object is created above the of loop
var iterator = makeIterator([1, 2, 3]);
for (let value of iterator) {
console.log(value); // Uncaught TypeError: iterator is not iterable
}
Copy the code
The iterator is not iterable. The iterator is not iterable. Of circulation
So what kind of structure is traversable?
Iterable Iterable
How to implement
ES6 also introduces a new Symbol object whose value is unique.
A data structure that deploys the symbol. iterator attribute is considered to have an Iterator interface; Calling this interface returns a traverser object. Such a data structure is called an Iterator, which can be called an Iterator for… Of traversal.
Create an iterable as described above
var arr = [1.2.3];
arr[Symbol.iterator] = function() {
var _this = this;
var i = 0;
return {
next: function() {
var done = (i >= _this.length);
varvalue = ! done ? _this[i++] :undefined;
return {
done: done,
value: value }; }}; }// For... Of traversal
for(var item of arr){
console.log(item); / / 1 2 3
}
Copy the code
For… The of iterator is the Symbol. Iterator property of the object.
A primitive data structure with an Iterator interface
In ES6, all collection objects, including arrays, array-like objects (arguments objects, DOM NodeList objects), maps and sets, and strings, are iterable and can be used for… Of traversal, because they all have default iterators.
Here are just a few examples:
An array of
var arr = [1.2.3];
var iteratorObj = arr[Symbol.iterator]();
console.log(iteratorObj.next());
console.log(iteratorObj.next());
console.log(iteratorObj.next());
console.log(iteratorObj.next());
Copy the code
Output result:
arguments
We know that normal objects do not deploy this interface by default, so the arguments property is not on the prototype, but on the properties of the object itself.
function test(){
var obj = arguments[Symbol.iterator]();
console.log(arguments);
console.log(obj.next());
console.log(obj.next());
console.log(obj.next());
}
test(1.2.3);
Copy the code
Output result:
NodeList
<div class="test">1</div>
<div class="test">2</div>
<div class="test">3</div>
Copy the code
const nodeList = document.getElementsByClassName('test')
for (const node of nodeList) {
console.log(node);
}
Copy the code
Output result:
map
This time it’s straight from the prototype
console.log(Map.prototype.hasOwnProperty(Symbol.iterator)); // true
Copy the code
For other types of iterables you can draw parallels.
The context in which the Iterator interface is called
There are situations where the Iterator interface (symbol. Iterator method) is called by default.
Deconstruction assignment
let set = new Set().add('a').add('b').add('c');
let [x, y] = set;
console.log(x, y); // a b
Copy the code
Extended operator
Extended operators (…) The default Iterator interface is also called to convert the current Iterator object to an array.
var str = 'hello';
console.log([...str]); // ["h", "e", "l", "l", "o"]
let arr = ['b'.'c'];
console.log(['a'. arr,'d']); // ["a", "b", "c", "d"]
Copy the code
yield*
Yield * is followed by a traversable structure, which invokes the traverser interface of that structure.
let generator = function* () {
yield 1;
yield* [2.3.4];
yield 5;
};
var iterator = generator();
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: 4, done: false }
console.log(iterator.next()); // { value: 5, done: false }
console.log(iterator.next()); // { value: undefined, done: true }
Copy the code
There will be another feature on Yield later
Other occasions
For example: the for… Of, Set(), Map(), array.from (), etc. The main point is to prove that these calls really use the Iterator interface.
We post the above code that implements iterables and make changes. Iterator is deployed by default on ES6, so the array can be iterated successfully. You can change the Symbol. You can redeploy the Symbol.
If our manual changes take effect and affect the output, prove that a method call needs to use the Iterator interface
var arr = [1.2.3];
arr[Symbol.iterator] = function() {
var _this = this;
var i = 0;
return {
next: function() {
var done = (i >= _this.length);
varvalue = ! done ? _this[i++] :undefined;
return {
done: done,
value: value + 'Manually added properties' // Add a custom value}; }}; }// The output shows that the manual modification takes effect, proving that for... Of calls the Iterator interface
for(var item of arr){
console.log(item);
// 1 Manually added attributes
// 2 Manually added attributes
// 3 Manually added attributes
}
var set = new Set(arr);
console.log(set); // Set(3) {"1 manually added attribute ", "2 manually added attribute ", "3 manually added attribute "}
Copy the code
The same is true of the other methods mentioned above, which are not listed.
Compute the generated data structure
Some data structures are computationally generated based on existing data structures. For example, ES6 arrays, sets, and maps all deploy the following three methods, which return a traverser object when called.
- Entries () returns an traverser object that iterates through an array of [key names, key values]. For arrays, the key name is the index value.
- Keys () returns a traverser object that iterates over all the key names.
- Values () returns a traverser object that iterates over all key values.
var arr = ["first"."second"."third"];
for (let index of arr.keys()) {
console.log(index);
}
/ / 0
/ / 1
/ / 2
for (let color of arr.values()) {
console.log(color);
}
// first
// second
// third
for (let item of arr.entries()) {
console.log(item);
}
// [ 0, "first" ]
// [ 1, "second" ]
// [ 2, "third" ]
Copy the code
Determines whether an object is iterable
const isIterable = obj= >obj ! =null && typeof obj[Symbol.iterator] === 'function';
console.log(isIterable(new Set())); // true
console.log(isIterable(new Map())); // true
console.log(isIterable([1.2.3])); // true
console.log(isIterable("hello world")); // true
Copy the code
for… The OF loop does not support traversing ordinary objects
Back to the beginning, Iterator was created primarily to use for… Of methods. Objects, unlike arrays, are ordered, and there is no way to determine the order in which they are iterated. The OF loop does not support traversing objects.
If an Object must be traversed, the Object must also contain a property of [symbol.iterator] and implement iterator methods, which can be added manually using the Object.defineProperty method.
var obj = { a: 2.b: 3 }
for (let i of obj) {
console.log(i) // Uncaught TypeError: obj is not iterable
}
Copy the code
That’s it for Iterator, next on Generator for ES6.
If this article is helpful to you, please give a thumbs up. If there are any mistakes, please correct them