The Iterator is an Iterator

1. Basic concepts

  • Js’s original data structure for “collections”, mainly arrays and objects. ES6 added Map and Set.
  • So there are four data structures that can be used to describe collections. But you need an interface system to handle all the different data structures
  • An Iterator is a mechanism, an interface that provides uniform access to different data structures
  • Once the Iterator interface is deployed on any data structure, traversal (processing all members of that data structure in turn) can be completed.
  • The Iterator interface does three things:
  1. It provides a unified and convenient access interface for various data structures in the form of collections
  2. Enables the members of a data structure to be sorted in some order
  3. ES6 creates a new way to iteratefor... Of circulation.The Iterator interface is designed primarily for... Of recycling!

2. Traversal methods

  • Iterator iterates by creating a pointer object and then calling the object’s next method each time, returning the value of the current member
  • What is returned is an object with two properties, value and done. Value is the value of the current member, and done is a Boolean that indicates whether the traversal is complete
  • In addition, done:false and vallue:undefined can be omitted, because if done is not returned, then undefined must be returned. If done is not returned, then undefined must be returned
  • Here is an implementation of one form of the Iterator interface:
  • It is important to note that in the native implementation of iterator, objects are not allowed to implement the iterator interface, i.e. The of loop will fail
 // 1. For an object, only the key whose index is key can be traversed
 function makeIterator(obj){
  var index=0;
  return {
   next:function(){
 return {  done:obj[index]?true:false. value:obj[index++]  }  }  }  }  var obj={0:'w'.1:444.5:555.a:'yy'}  var res1=makeIterator(obj)  console.log(res1.next());//{done: true, value: "w"}  console.log(res1.next());//{done: true, value: 444}  console.log(res1.next());//{done: false, value: undefined}  console.log(res1.next());//{done: false, value: undefined}  // And note that if the index is a number, it is not necessarily traversable, because depending on the value of index, if you want to traverse to 5, you need to call next twice more   // 1.2 But without overriding the iterator interface (using the function as above, or overriding the Symbol  // Use for objects. The of loop will report an error!  for(var item of {0:1.1:22{}) console.log(item);//Uncaught TypeError: {(intermediate value)(intermediate value)} is not iterable  }   // 2. For arrays  function makeArray(arr){  var index=0;  return {  next:function(){  return {  done:arr[index]?true:false. value:arr[index++]  }  }  }  }  var res2=makeArray([5.4.3])  console.log(res2.next())//{done: true, value: 5}  console.log(res2.next())//{done: true, value: 4}  console.log(res2.next())//{done: true, value: 3}  console.log(res2.next())//{done: false, value: undefined} Copy the code

Default Iterator interface

1. Basic concepts

  • The purpose of the Iterator interface is to provide a uniform access mechanism for all data structures. Of circulation
  • When using the for… When the of loop iterates over a data structure, it automatically looks for the Iterator interface
  • A data structure is as long as the Iterator interface is deployedIterable
  • Whereas ES specifies that the default Iterator interface is deployed atSymbol. The iterator attributeSo we can judge byWhether there is a Symbol. Iterator attribute as a basis for traversability
  • Iterator is a Symbol. Iterator is a Symbol. Iterator is an expression, like [1+'2']
   / / 1. Array
  var arr=[]
  console.log(arr[(Symbol.iterator)])Logon values() {[native code]}
  // 2. Map
  var map=new Map(a) console.log(map[Symbol.iterator]);Logon () {[native code]}  // 3. Set  var set=new Set()  console.log(set[Symbol.iterator]); Logon values() {[native code]} // 4. Object (no iterator interface)  var obj={}  console.log(obj[Symbol.iterator]);//undeined  // 5. Objects that add numeric properties do not have a terator interface unless you modify the object's Symbol  var obj2={0:1.1:222}  console.log(obj2[Symbol.iterator]);//undeined  // 6. Strings also have an iterator interface  var str="hello world"  console.log(str[Symbol.iterator]);Logon [Symbol. Iterator]() {[native code]}  // 7. The number is not  var nums=1234  console.log(nums[Symbol.iterator]);//undeined  // 8. Arguments, function argument array has!  function func(){  console.log(arguments[Symbol.iterator]);Logon values() {[native code]}  }  func(1.4.8)  // 9. Nodeslist Node list has!  console.log(document.getElementsByClassName("one") [Symbol.iterator]);Logon values() {[native code]} Copy the code
  • Calling the Symbol. Iterator property returns an iterator object with the next method, which returns an object with the value and done properties
  • From above, data with an iterator interface is:Array,String,Nodelist,arguments,Map,Set

2. Practical application

  • For data structures where the Symbol. Iterator property is deployed natively, there is no need to write the iterator generator.for... The of loop automatically traverses them
  • Other data structures that do not have an iterator interface are requiredIterator is deployed on the Symbol. Of loop to
  • There are two ways to loop through
  var arr=[4.6.8]
  // 1. for... of
  for(var item of arr){
   console.log(item);
  }
  // 2. Call Symbol. Iterator  var res=arr[Symbol.iterator]()  console.log(res.next())//{value: 4, done: false}  console.log(res.next())//{value: 6, done: false}  console.log(res.next());//{value: 8, done: false}  console.log(res.next());//{value: undefined, done: true} Copy the code
  • The reason an iterator interface is not deployed on an object: it is uncertain which property of the object is traversed first and which property is traversed next.
  • Ergodic is essentially a linear process, and deploying an ergodic interface for any nonlinear data structure is deploying a linear transformation.
  class RangeIterator {
    constructor(start, stop) {
      this.value = start;
      this.stop = stop;
    }
  [Symbol.iterator]() {  console.log("Call the Symbol. Iterator property")  return this;  }   next() {  console.log("Call the next property")  var value = this.value;  if (value < this.stop) {  this.value++;  return {done: false.value: value};  }  return {done: true.value: undefined};  }  }   function range(start, stop) {  return new RangeIterator(start, stop);  }  // Get an object  console.log(range(0.3));//RangeIterator {value: 0, stop: 3}  / / then the for... Of walking through the object is equivalent to calling the object's next method until done is true  console.log(range(0.3).__proto__);  /* The stereotype of this instance has the following properties constructor: class RangeIterator Next: ƒ next ()Symbol (Symbol. The iterator) : ƒ / Symbol. The iterator () __proto__: Object * /  for (var value of range(0.3)) {  console.log(value); / / 0, 1, 2  } Copy the code

3. Deploy the ITrator interface to the object

  • We actually deploy the ierator interface to an object by calling the array's iterator property directly
  • Implementing the iterator interface as in section 1 is not recommended.And you have to add the length attribute in order for... The of loop gets the value
  • An array-like object is an object that has a length property, so the length property must have, which determines the number of loops
  • Deployment mode:Set the object's Symbol. Iterator property to the array's Symbol. Iterator property. [Symbol.iterator]:Array.prototype[Symbol.iterator]
  • Example:
 // 1. The object has 0,1,2 numeric properties
 var obj1={
  0:'a'.  1:3333.  2:'w'. length:3. [Symbol.iterator]:Array.prototype[Symbol.iterator]  }  for(var item of obj1){  console.log(item);//a,3333,w   }   // 2. No number attribute  var obj2={  length:3. [Symbol.iterator]:Array.prototype[Symbol.iterator]  }  for(var item of obj2){  console.log(item);//undefined,undefined,undefined  }   // 3. No length attribute (nothing is printed at this time).  var obj3={  [Symbol.iterator]:Array.prototype[Symbol.iterator]  }  for(var item of obj3){  console.log(item);  }   // 4. It has a non-digital attribute and no length attribute (it does not print).  var obj4={  a:'a'. [Symbol.iterator]:Array.prototype[Symbol.iterator]  }  for(var item of obj4){  console.log(item);  }   // 5. It has non-number and length attributes  // It will execute the number of times the length recorded by length, but there is no corresponding index property, and only unepaid will be obtained  var obj5={  a:'a'. length:1. [Symbol.iterator]:Array.prototype[Symbol.iterator]  }  for(var item of obj5){  console.log(item);// undefined  } Copy the code

Three. Application occasions

1. Deconstruct assignments

  • The Symbol. Iterator interface is called by default
 var set=new Set().add('a').add('b').add('c')
 let [x,y]=set;
console.log(x,y); //a,b let [a,...b]=set;
console.log(a,b); //a,['b','c']Copy the code

2. Expand operators

  • The extension operator also calls the default Symbol. Iterator interface
 var str="hello"
 console.log([...str]);//["h", "e", "l", "l", "o"]
 var arr=['a'.2.6]
 console.log([...arr]);//["a", 2, 6]
Copy the code

3. yield*

  • Yield * is followed by a traversable structure, and the traverser interface for that structure is called
  var gen=function* (){
   yield 1;
   yield* [2.3.4];
   yield 9
  }
 var res=gen();  console.log(res.next())//{value: 1, done: false}  console.log(res.next())//{value: 2, done: false}  console.log(res.next())//{value: 3, done: false}  console.log(res.next())//{value: 4, done: false}  console.log(res.next());//{value: 9, done: false}  console.log(res.next());//{value: undefined, done: true} Copy the code

Iterator interface for strings

  • A string is also an array-like object,It also has a length propertyAlso,The iterator interface is native
 var str="hi"
 // The Symbol. Iterator property is a function that returns a iterator object
 console.log(typeof str[Symbol.iterator]);//function
 
 var res=str[Symbol.iterator]();
 console.log(res.next());//{value: "h", done: false}  console.log(res.next());//{value: "i", done: false}  console.log(res.next());//{value: undefined, done: true} Copy the code
  • The Symbol. Iterator interface can be overwritten
 // var str="hello"
 var str=new String("hello")
 console.log([...str]);//["h", "e", "l", "l", "o"]
 str[Symbol.iterator]=function* (){
  yield 1;
 yield 2;  yield 3;  }  console.log([...str]);/ / [1, 2, 3] Copy the code

The Iterator interface is combined with generator functions

  • IteratorSimplest implementation: Combined with generator functions
 / / form 1
 var res1={
  [Symbol.iterator]:function* (){
   yield 1;
   yield 11;
 yield 111;  }  }  console.log([...res1]);/ / [1, 11, 111]   2 / / form  var res2={ * [Symbol.iterator](){  yield 2;  yield 22;  yield 222;  }  }  console.log([...res2]);/ / (2, 22, 222] Copy the code

Vi. Retrun () and throw()

  • In addition to having the next method, the traverser object can also haveReturn method and throw method
  • If it isCustom Symbol. Iterator methods, then return() and throw() are optional
  • The return method infor... If the of loop exits early, the return method is called
  • And the return method must return an object! The throw method is used primarily with generator functions
 / / return method
 var arr=[2.4.6]
 arr[Symbol.iterator]=function(){
  return {
   next(){
 return {value:'a'.done:false}  },  return() { return {done:true}  }  }  }  var i=0;  for(var item of arr){  i++;  console.log(item);//a,a,a,a  if(i>3) { // Call the return method in the first way :break  // break;  // 2. Throw an error  throw new Error("err");//Error: err  }  } Copy the code

7. The for… Of circulation

  • A data structure that has a Symbol. Iterator attribute is considered to have an iterator interface. The of loop iterates through its members
  • for... Inside the of loop, the Symbol. Iterator method of the variable itself is called
  • for… The in loop reads the key name, for… Of is used to read key values.
  • And the for... Of calls the traverser interface (only properties with numeric indexes are returned), while for.. In does not call the traverser interface, so properties that are not numerically indexed can be obtained
 var arr=[1.2.5]
 arr.foo="ffff"
 
 // for... The of loop cannot obtain attributes that are not numerically indexed
 for(var item of arr){
 console.log(item)  / / 1,2,5  }   // for ... in  for(var i in arr){  console.log(arr[i])  // You can get an attribute indexed to a number  / / 1,2,5 FFFF  } Copy the code

An array of 1.

  • Arrays have an iterator interface native to them
 var arr=['a'.2.7]
 for(var item of arr){
  console.log(item)/ / a, 2, 7
 }
 var obj={}
 // Set the array's Symbol. Iterator interface to the object  // Use bind instead of calling this method  obj[Symbol.iterator]=arr[Symbol.iterator].bind(arr);  for(var item of obj){  console.log(item);/ / a, 2, 7  }   // Add new properties to the object  obj['a'] ='yy'  obj[3] =3  // Notice that the attribute is not added to the Symbol  console.log(obj);//{3: 3, a: "yy", Symbol(Symbol. Iterator): ƒ}  BoundThis has no attribute a,3  / * arguments: [Exception: TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them at Function.invokeGetter (<anonymous>:1:142)]  caller: [Exception: TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them at Function.invokeGetter (<anonymous>:1:142)]  length: 0  name: "bound values" __proto__ : ƒ ()[[TargetFunction]] : ƒ values () [[BoundThis]]: Array(3)  0: "a" 1:22:7 length: 3  __proto__: Array(0)  [[BoundArgs]]: Array(0) * /  for(var item of obj){  // There is no traversal to the attributes added later  console.log(item);/ / a, 2, 7  } Copy the code

2.Set and Map structure

  • The Set and Map structures are native to the Iterator interface and can be used directly with the for… Of circulation
 // 1. set
 var set=new Set()
 set.add(1)
 set.add(10)
 set.add('a')
 for(var item of set) { console.log(item);/ / 1, 10, a  }   // 2. Map   var map=new Map(a); map.set('a'.1)  map.set('ab'.11)  map.set('ba'.1081)  for(var item of map){  console.log(item)  // ["a", 1],["ab", 11],["ba", 1081]  } Copy the code

3. Array-like objects

  • Array-like objects includeNodelist,arguments, string...
 // 1. A string
 var str="hello"
 for(var item of str){
  console.log(item);//h,e,l,l,o 
 }
 // 2. Nodelist object  var nodes=document.getElementsByClassName('one')  for(var item of nodes){  console.log(item);//<div class="one"></div>  }  // 3. Arguments object  function args(){  for(var item of arguments) { console.log(item);//5,'w',98  }  }  args(5.'w'.98) Copy the code
  • But for objects, even if you add the length attribute to make an array-like object
  • You also need to override the Symbol. Iterator method to loop through the properties.Or use the array. from method to convert to an Array first
 var obj={length:2.0:1.1:'e'.t:333}
 // Although an array-like object, there is no native Symbol. Iterator method, and there is no override
 // So the error is still reported
 /* for(var item of obj){
console.log(item); //TypeError obj is not iterable} * /   // Solution 1, set Symbol. Iterator method /* obj[Symbol.iterator]=Array.prototype[Symbol.iterator]  for(var item of obj){ console.log(item); //1,e} * /   // Solution 2: Use array. from to convert to Array  obj=Array.from(obj)  for(var item of obj){  console.log(item);//1,e  } Copy the code
  • If you do not want to add the attribute Symbol. Iterator or convert it to an array, then
  • Methods:1. Through the for... The in loop gets the key value indirectly. 2. Iterate through the array generated by object.keys ()
 var obj={a:2.0:'ss'.1:111}
 // 1. for... In circulation
 for(var i in obj){
  console.log(obj[i]);/ / ss, 111, 2
 }
 // 2.object.keys () generates an array  for(var item of Object.keys(obj)){  console.log(obj[item])/ / ss, 111, 2  } Copy the code

4. The forEach loop

  • ForEach loops have the advantage of being simple to write, and the disadvantageDo not use break,continue,return to exit!
 var arr=[4.5.77.0.323]
 arr.forEach((item,i) = >{
  console.log(item);/ / 4,5,77,0,323
  if(i==2) {   // return ; // Failed to exit. Don't complain
 // break; // 错 误, Illegal break statement  // continue; // Error :Illegal continue Statement: no surrounding iteration statement  }  }) Copy the code

Refer to ruan Yifeng’s ES6 tutorial for this article

This article was typeset using MDNICE