How do I iterate over elements of an array? Twenty years ago, when JavaScript came along, you might have done this:

for (var index = 0; index < myArray.length; index++) {
  console.log(myArray[index]);
}Copy the code

Since ES5, you can use the built-in forEach method:

myArray.forEach(function (value) {
  console.log(value);
});Copy the code

The code is leaner, but there is a small drawback: you can’t use a break statement to break out of a loop, nor can you use a return statement to return from a closure function.

It would be much easier to iterate over groups of numbers if you had the syntax for-.

So, what about for-in?

Console. log(myArray[index]); for (var index in myArray) {console.log(myArray[index]); }Copy the code

This is not good because:

Simply put, for-Ins are designed to iterate over objects that contain key-value pairs, and are not that array friendly.

Powerful for-of loops

Remember I mentioned last time that ES6 doesn’t affect how existing JS code works, there are thousands of Web applications that rely on for-In features, and even for-In features for arrays, so no one has ever proposed “improving” the existing for-In syntax to fix this problem. ES6’s only solution to this problem is to introduce a new looping traversal syntax.

Here’s the new syntax:

for (var value of myArray) {
  console.log(value);
}Copy the code

By introducing the for-in syntax above, it doesn’t look all that impressive. We’ll talk more about the wonders of for-of later, but for now, all you need to know is:

  • This is the easiest and most straightforward way to iterate over a set of numbers
  • Avoid allThe for - inSyntax exists in the pit
  • withforEach()The difference is that it supportsbreak,continuereturnStatements.

For-in is used to traverse the properties of an object.

For-of is used to iterate over data — just like elements in an array.

However, that’s not all that for-of has to offer. Here’s the kicker.

Other collections that support for-of

For-of isn’t just designed for arrays. It can also be used for array-like objects, such as NodeList, a collection of DOM objects.

It can also be used to iterate over strings, treating them as collections of Unicode characters:

For (var CHR of "😺😲") {alert(CHR); }Copy the code

It also applies to Map and Set objects.

You may never have heard of Map and Set objects, as they are new to ES6 and will be covered in a separate article. If you have used these two objects in other languages, it is much easier.

For example, we can use a Set object to deduplicate array elements:

// make a set from an array of words
var uniqueWords = new Set(words);Copy the code

When you get a Set object, you’ll probably iterate over it. This is simple:

for (var word of uniqueWords) {
  console.log(word);
}Copy the code

Map objects are made up of key-value pairs, traversal slightly differently, and you need two separate variables to receive keys and values respectively:

for (var [key, value] of phoneBookMap) {
  console.log(key + "'s phone number is: " + value);
}Copy the code

By now, you already know that JS already supports a few collection objects, and there will be more to come. The for-of syntax is designed for these collection objects.

For-of cannot be used directly to iterate over an object’s properties. If you want to iterate over an object’s properties, you can either use the for-in statement (which is what for-in does), or use the following method:

// dump an object's own enumerable properties to the console
for (var key of Object.keys(someObject)) {
  console.log(key + ": " + someObject[key]);
}Copy the code

internals

“Good artists copy, great artists steal.” “– Pablo Picasso

The new features that have been added to ES6 aren’t out of the blue; most have already been used in other languages and are proving useful.

In the case of for-of, similar looping statements exist in C++, JAVA, C#, and Python and are used to iterate over various data structures in the language and its standard library.

As with the for and foreach statements in other languages, for-of requires that the object being traversed implement specific methods. All Array, Map, and Set objects have one thing in common: they implement an iterator method.

Then, you can implement an iterator method for any other object you want.

Just as you can implement a myObject.toString () method for an object to tell the JS engine how to convert an object to a string; You can also implement a myObject[symbol.iterator]() method for any object to tell the JS engine how to iterate over the object.

For example, if you’re using jQuery and you really like using its each() method, and now you want all jQuery objects to support for-of statements, you can do this:

// Since jQuery objects are array-like,
// give them the same iterator method Arrays have
jQuery.prototype[Symbol.iterator] =
  Array.prototype[Symbol.iterator];Copy the code

You may be wondering why the [symbol. iterator] syntax looks so strange. What exactly does that mean? The crux of the matter is the method name. The ES Standards committee could have named the method iterator(), but there might already be a method named “iterator” in an existing object, which would clutter up the code and violate maximum compatibility. So the standards committee introduced Symbol, not just a string, as a method name.

Symbol is also new to ES6 and will be covered in a separate article. For now, all you need to know is that the standards committee introduced new symbols, such as symbol. iterator, so as not to conflict with the previous code. The only downside is that the syntax is a bit strange, but that’s a minor factor given this powerful new feature and perfect backward compatibility.

An object with the [symbol.iterator]() method is considered iterable. In later articles, we will see that the concept of “traversable objects” is used throughout the language, not only in for-of statements, but also in the constructors and Destructuring functions of maps and sets, and in the new extension operators.

Iterator object

We don’t usually implement an Iterator from scratch, and the next article will show you why. But for the sake of completeness, let’s look at what an iterator object looks like. (If you skip this section, you will miss out on some technical details.)

For example, the for-of statement first calls the [symbol.iterator]() method of the iterated collection object, which returns an iterator object, which can be any object that has a.next method; The.next method on that iterator object is then called each time through the for-of loop. Here is the simplest iterator object:

var zeroesForeverIterator = { [Symbol.iterator]: function () { return this; }, next: function () { return {done: false, value: 0}; }};Copy the code

In the above code, each time the.next() method is called, the same result is returned, which tells the for-of that the loop is not finished and that the value of the loop is 0. This means that for (value of zeroesForeverIterator) {} is an infinite loop. Of course, a typical iterator is not so simple.

ES6 iterators use the.done and.value attributes to identify the result of each iteration. This is how iterators are designed, unlike iterators in other languages. In Java, iterator objects use.hasNext() and.next() methods, respectively. In Python, iterator objects have only one.next() method, which raises a StopIteration exception when there are no elements to iterate over. But fundamentally, all three designs return the same information.

Iterator objects can optionally implement the.return() and.throw(exc) methods. If the loop exits prematurely due to an exception or the use of the break and return operators, the iterator’s.return() method will be called. The.return() method can be implemented to free up resources occupied by the iterator object, but most iterators are not required to implement this method. Throw (exc) is even more of a special case: it is never called during traversal, which I’ll cover in more detail in the next article.

Now that we know all the details of for-of, we can simply rewrite the statement.

The first is the for-of body:

for (VAR of ITERABLE) {
  STATEMENTS
}Copy the code

This is just a semantic implementation, using some low-level methods and a few temporary variables:

var $iterator = ITERABLE[Symbol.iterator](); var $result = $iterator.next(); while (! $result.done) { VAR = $result.value; STATEMENTS $result = $iterator.next(); }Copy the code

The code above doesn’t cover how to call the.return() method, we can add handlers, but I think that would affect our understanding of the internals. For-of statements are very simple to use, but have a lot of detail inside them.

compatibility

Currently, for-of statements are supported in all Firefox releases. Chrome disables this statement by default, so you can go to the Settings page by typing Chrome ://flags in the address bar and check the “Experimental JavaScript” option. Microsoft’s Spartan browser also supports this statement, but Internet Explorer does not. If you want to use this statement in Web development and need compatibility with IE and Safari, you can use a compiler like Babel or Google’s Traceur to convert ES6 code into Web-friendly ES5 code.

On the server side, we don’t need any compilers – we can use this statement directly in io.js, or use the — Harmony startup option when NodeJS starts.

{done: true}

That’s the end of today’s topic, but not the end of for-of.

There is also a new object in ES6 that works perfectly with for-of statements, but I didn’t mention it today because it’s the subject of the next article, and I think this new object is the biggest feature in ES6. If you haven’t already touched this object in Python or C#, you’ll think this is fantastic, but it’s the easiest way to write an iterator, and it’s very useful for code refactoring, and it may change the way we handle asynchronous code. So stay tuned for my next discussion of generators.

ES6 In Depth: Iterators and the For-of Loop ES6 In Depth: Iterators and the For-of Loop