The origin of

A standard for loop:

var colors = ["red"."green"."blue"];

for (var i = 0, len = colors.length; i < len; i++) {
    console.log(colors[i]);
}
Copy the code

Looking at very simple, but again to review this code, actually we just need the value of the elements in the array, but need to get ahead of the array length, statement index variable, etc., especially when multiple nested loop, more need to use multiple index variables, the complexity of the code will be greatly increased, for example, we use a double cycle are to:

function unique(array) {
    var res = [];
    for (var i = 0, arrayLen = array.length; i < arrayLen; i++) {
        for (var j = 0, resLen = res.length; j < resLen; j++) {
            if (array[i] === res[j]) {
                break; }}if(j === resLen) { res.push(array[i]); }}return res;
}
Copy the code

To eliminate this complexity and reduce errors in loops (such as incorrectly using variables from other loops), ES6 provides iterators and for of loops to solve this problem.

The iterator

An iterator is an object with a next() method. Each call to next() returns a result object with two attributes, value indicating the current value and done indicating whether the traversal is complete.

We create an iterator directly using ES5 syntax:

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 }
Copy the code

for of

In addition to iterators, we also need a way to iterate over iterators. ES6 provides for of statements, so let’s use for of to iterate over the iterator object we generated in the previous section:

var iterator = createIterator([1.2.3]);

for (let value of iterator) {
    console.log(value);
}
Copy the code

TypeError: Iterator is not iterable, indicating that the generated iterator is not iterable.

So what is traversable?

In fact, a data structure is said to be “iterable” whenever the Iterator interface is deployed.

ES6 states that the default Iterator interface is deployed in the symbol. Iterator property of data structures, or that a data structure can be considered “iterable” as long as it has the symbol. Iterator property.

Here’s an example:

const obj = {
    value: 1
};

for (value of obj) {
    console.log(value);
}

// TypeError: iterator is not iterable
Copy the code

If we iterate over an object directly for of, we get an error, but if we add symbol. iterator to the object:

const obj = {
    value: 1
};

obj[Symbol.iterator] = function() {
    return createIterator([1.2.3]);
};

for (value of obj) {
    console.log(value);
}

/ / 1
/ / 2
/ / 3
Copy the code

We can also see that the for of traversal is the Symbol. Iterator property of the object.

Objects are traversable by default

However, if we directly iterate over an array object:

const colors = ["red"."green"."blue"];

for (let color of colors) {
    console.log(color);
}

// red
// green
// blue
Copy the code

Although we don’t have to manually add Symbol. The iterator attribute, or can traverse the success, this is because the default deployment ES6 Symbol. The iterator attribute, of course, we can also manually modify this property:

var colors = ["red"."green"."blue"];

colors[Symbol.iterator] = function() {
    return createIterator([1.2.3]);
};

for (let color of colors) {
    console.log(color);
}

/ / 1
/ / 2
/ / 3
Copy the code

In addition to arrays, some data structures deploy the symbol.iterator property by default.

So the for… The scope of the OF loop includes:

  1. An array of
  2. Set
  3. Map
  4. Class array objects, such as arguments objects, DOM NodeList objects
  5. The Generator object
  6. string

Simulation implementation for of

Iterator () : iterator () : iterator () : iterator () : iterator () : iterator () : iterator () : iterator ();

function forOf(obj, cb) {
    let iterable, result;

    if (typeof obj[Symbol.iterator] ! = ="function")
        throw new TypeError(result + " is not iterable");
    if (typeofcb ! = ="function") throw new TypeError("cb must be callable");

    iterable = obj[Symbol.iterator]();

    result = iterable.next();
    while (!result.done) {
        cb(result.value);
        result = iterable.next();
    }
}
Copy the code

Built-in iterators

In order to better access the contents of objects, for example, sometimes we need only the values in the array, but sometimes we need to use not only the values but also the indexes. ES6 builds the following iterators for arrays, maps, and sets:

  1. Entries () returns an traverser object that iterates through an array of [key names, key values]. For arrays, the key name is the index value.
  2. Keys () returns a traverser object that iterates over all the key names.
  3. Values () returns a traverser object that iterates over all key values.

Take arrays as an example:

var colors = ["red"."green"."blue"];

for (let index of colors.keys()) {
    console.log(index);
}

/ / 0
/ / 1
/ / 2

for (let color of colors.values()) {
    console.log(color);
}

// red
// green
// blue

for (let item of colors.entries()) {
    console.log(item);
}

// [ 0, "red" ]
// [ 1, "green" ]
// [ 2, "blue" ]
Copy the code

The Map type is similar to an array, but the Set type requires the following precautions:

var colors = new Set(["red"."green"."blue"]);

for (let index of colors.keys()) {
    console.log(index);
}

// red
// green
// blue

for (let color of colors.values()) {
    console.log(color);
}

// red
// green
// blue

for (let item of colors.entries()) {
    console.log(item);
}

// [ "red", "red" ]
// [ "green", "green" ]
// [ "blue", "blue" ]
Copy the code

Keys () and values() of Set return the same iterator, which means that the key name is the same as the key value in a data structure like Set.

And each collection type has a default iterator, which is used in for-of loops if not explicitly specified. The default iterator for arrays and sets is the values() method, and the default iterator for Map is the entries() method.

This is why for of traverses Set and Map data structures directly and returns different data structures:

const values = new Set([1.2.3]);

for (let value of values) {
    console.log(value);
}

/ / 1
/ / 2
/ / 3
Copy the code
const values = new Map([["key1"."value1"], ["key2"."value2"]]);
for (let value of values) {
    console.log(value);
}

// ["key1", "value1"]
// ["key2", "value2"]
Copy the code

When traversing the Map data structure, we can also use destruct assignment:

const valuess = new Map([["key1"."value1"], ["key2"."value2"]]);

for (let [key, value] of valuess) {
    console.log(key + ":" + value);
}

// key1:value1
// key2:value2
Copy the code

How does Babel compile for of

We can see the result of the compilation in Babel’s Try it out:

const colors = new Set(["red"."green"."blue"]);

for (let color of colors) {
    console.log(color);
}
Copy the code

For such a piece of code, the result of compilation is as follows:

"use strict";

var colors = new Set(["red"."green"."blue"]);

var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;

try {
    for (
        var _iterator = colors[Symbol.iterator](), _step; ! (_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion =true
    ) {
        var color = _step.value;

        console.log(color); }}catch (err) {
    _didIteratorError = true;
    _iteratorError = err;
} finally {
    try {
        if (!_iteratorNormalCompletion && _iterator.return) {
            _iterator.return();
        }
    } finally {
        if (_didIteratorError) {
            throw_iteratorError; }}}Copy the code

At least the result of the compilation shows that the Symbol. Iterator interface is still used behind the for of loop.

The compiled code is slightly more complicated in two sections, one of which is the for loop here:

for (
    var _iterator = colors[Symbol.iterator](), _step; ! (_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion =true
) {
    var color = _step.value;
    console.log(color);
}
Copy the code

This is a bit different from the standard for loop. Let’s look at the syntax of the for statement:

for (initialize; test; increment) statement;
Copy the code

Initialize, test, and increment are separated by a semicolon and are responsible for initialization, loop condition determination, and counter variable updates, respectively.

The for statement is the same as:

initialize;
while (test) {
    statement;
    increment;
}
Copy the code

The logic of the code is as follows: the test expression is executed before each loop execution, and the result of the expression is evaluated to determine whether the loop body is executed. If test evaluates to true, the statement in the loop body is executed. Finally, execute the increment expression.

It is also worth noting that any of the three expressions in the for loop can be ignored, but the semicolon is still needed.

As for (;;) But it’s a constant cycle…

Such as:

var i = 0,
    len = colors.length;
for (; i < len; i++) {
    console.log(colors[i]);
}
Copy the code

Such as:

var i = 0,
    len = colors.length;
for (; i < len; ) {
    i++;
}
Copy the code

Then let’s look at this for loop compiled by Babel:

for (
    var _iterator = colors[Symbol.iterator](), _step; ! (_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion =true
) {
    var color = _step.value;
    console.log(color);
}
Copy the code

While equivalent to:

var _iterator = colors[Symbol.iterator](),
    _step;
while(! (_iteratorNormalCompletion = (_step = _iterator.next()).done)) {var color = _step.value;
    console.log(color);
    _iteratorNormalCompletion = true;
}
Copy the code

Then you’ll see that _iteratorNormalCompletion = true is completely unnecessary…

Another slightly more complicated piece of code is:

try{... }catch (err) {
  ...
} finally {
  try {
    if (!_iteratorNormalCompletion && _iterator.return) {
      _iterator.return();
    }
  } finally{... }}Copy the code

Since _iteratorNormalCompletion = (_step = _iterator.next()).done, _iteratorNormalCompletion indicates whether a complete iteration has been completed. This method is executed if no normal iteration has been completed and the iterator has a return method.

To do so, refer to the iterator’s return method.

Introduction to ECMAScript 6 by Ruan Yifeng

An iterator object can have return and throw methods in addition to next methods. If you write your own traverser object generating function, the next method must be deployed, and the return and throw methods are optional.

The return method is used if for… When the of loop exits prematurely (usually because of an error, or with a break or continue statement), the return method is called. The return method can be deployed if an object needs to clean up or release resources before completing traversal.

Here’s an example:

function createIterator(items) {
    var i = 0;
    return {
        next: function() {
            var done = i >= items.length;
            varvalue = ! done ? items[i++] :undefined;

            return {
                done: done,
                value: value
            };
        },
        return: function() {
            console.log("Return method executed");
            return {
                value: 23333.done: true}; }}; }var colors = ["red"."green"."blue"];

var iterator = createIterator([1.2.3]);

colors[Symbol.iterator] = function() {
    return iterator;
};

for (let color of colors) {
    if (color == 1) break;
    console.log(color);
}
// The return method is executed
Copy the code

But as you can see in the compiled code, the value returned by the return function does not actually take effect, just by executing the return function when it is present…

But if you do not return a value or a value of a primitive type, the result will be an error…

TypeError: Iterator result undefined is not an object
Copy the code

This is because the return method must return an object, which is determined by the Generator specification…

Anyway, if you’re using a browser, the return value of the return function doesn’t actually apply T^T

ES6 series

ES6 directory address: github.com/mqyqingfeng…

ES6 series is expected to write about 20 chapters, aiming to deepen the understanding of ES6 knowledge points, focusing on the block-level scope, tag template, arrow function, Symbol, Set, Map and Promise simulation implementation, module loading scheme, asynchronous processing and other contents.

If there is any mistake or not precise place, please be sure to give correction, thank you very much. If you like or are inspired by it, welcome star and encourage the author.