preface

Iterator is a new JavaScript feature in ES6. It contains Iterator property data for which JS can execute commands such as for… Of,map(),filter() and other iterative methods. With this skill in hand, iterators can be used to make more appropriate technology choices in some development business situations.

Iterative agreement

It’s worth noting that in ES6 we’ve seen a lot of new apis built around iterators. As for… Of the map (), the filter (), etc. But the implementation of these methods, not ES added some built-in implementation or syntax. All ES did was customize a set of iteration protocols.

This iteration protocol has two parts, the iterable protocol and the iterator protocol.

Iterator protocol

The iterator protocol specifies what an iterator should look like. Officially, an object must have a Next method in order to be called an iterator. The next method call returns an object. This object has two properties value and done.

  • Value is the return value of this iteration. After done is true, value is meaningless, but it is better to return undefined.
  • Done indicates the end of an iteration.

Based on this specification, we can roughly write an iterator that looks like this:

var myIterator = {
  next:function(){count++;if(count<3) {return {value: count,done:false};
    }else{
      return {value: undefined.done:true}; }},count:0
}
Copy the code

Iterable protocol

But having an iterator isn’t going to work. We need a carrier to make the iterator work. That’s what the Iterable Protocol does. The iterable protocol specifies what data can be used as a vehicle for running iterators. In JavaScript, String, Array, TypedArray, Map, and Set are all built-in iterable protocol compliant types.

One of the most common ways to invoke iterations is for… Of.

for(var i of [1.2.3]) {console.log(i);
}

for(var i of "123") {console.log(i);
}

for(var i of new Set([1.2.3])){
	console.log(i);
}
// All the above output
/ / 1
/ / 2
/ / 3
Copy the code

Symbol.iterator

Ok, now we know that some common types can call iterative methods because they comply with the iterable protocol. But what exactly is this agreement? Can we manually make other types conform to this protocol?

The answer is the @@iterator method, and only one piece of data that has an @@ Iterator on it is iterable protocol compliant. This method can be referenced by the Symbol. Iterator property.

To do this, we can print array, map, set, etc. in the browser console. You can see that they all have a Symbol. Iterator property built in.

Therefore, we can simply think of a single data (no matter what inner class) that has the Symbol. Iterator property on it, and that property refers to an iterator that conforms to the iterator protocol. This data complies with the iterable protocol.

Implement Iterator manually

At this point, we’ve actually done all the preparatory work. Now let’s start manually implementing the for of method for an object that is not a built-in iterator protocol compliant data type.

Object types in JS do not conform to the iterable protocol by default
var obj = {a:'a'.b:'b'};
for(var i of obj){
	console.log(i);
}
Uncaught TypeError: obj is not iterable
Copy the code

Let’s write a repeater.

// Write an iterator according to the iterator protocol
var myIterator = function(){
  var done = false;
  return {
    next: function(){
      if(done){
      	return {value:'hi'.done:true};
      }else{
        done = true;
      	return {value:'going'.done:false}; }}}}Copy the code

We then add the Symbol. Iterator property to an object and point to the iterator written above.

var obj={};
obj[Symbol.iterator] = myIterator;
Copy the code

Call for… of

for(var i of obj){
	console.log(i);
}
// Normal output
// 'going'
Copy the code

As you can see, obj can already execute for… like a type with built-in conforming iterators. Of the.

Combine generator optimization writing method

If you look at the Next method, you’ll think of generators. Since iterators are constantly calling next methods, and generator functions naturally come with next methods, isn’t it possible to define iterators using generators directly? The answer is yes, with generators we can write much more concise code.

var myIterator = function* (){
  	yield 11;
  	yield 12;
  	yield 13;
  	yield 14;
  	yield 15;
}

// Define an empty array
var arr = [];
arr[Symbol.iterator] = myIterator;
for(var i of arr){
	console.log(i);
}
/ / output
/ / 11
/ / 12
/ / 13
/ / 14
/ / 15
Copy the code

So is a generator an object or a function?

After simplifying the implementation above, we found that the combination of generators did achieve the desired effect. But didn’t we say earlier in this article that iterators must conform to the iterator protocol? Shouldn’t an iterator be an object? Why is a generator a function here that also fits this protocol?

No doubt about it, because generators are both functions and objects.

// Define a generator
let aGeneratorObject = function* (){
    yield 1;
    yield 2;
    yield 3; } ();typeof aGeneratorObject.next;
// return "function", which has a next method, indicating that it complies with the iterator protocol

typeof aGeneratorObject[Symbol.iterator];
// return "function" with an @@iterator method, indicating that the generator itself is an iterable

aGeneratorObject[Symbol.iterator]() === aGeneratorObject;
// Returns true, because the @@iterator method returns itself (that is, an iterator), indicating that this is a well-formed iterable

[...aGeneratorObject];
// Return [1, 2, 3]

console.log(Symbol.iterator in aGeneratorObject)
// Returns true, because the @@iterator method is an attribute of the aGeneratorObject
Copy the code

conclusion

Today we learned about the iterator protocol in JavaScript and how iterators relate to iterables. And implement the effect of custom iterator manually. Hope to help you have a deep understanding of the iterator, and use it in the development.

reference

Developer.mozilla.org/zh-CN/docs/…

Developer.mozilla.org/zh-CN/docs/…

Developer.mozilla.org/zh-CN/docs/…