Learn more about the for loop in JavaScript

www.codeceo.com

In ECMAScript5 (ES5 for short), there are three for loops:

In ECMAScript6 (ES6 for short) released in June 2015, a new type of loop was added, which is:

Let’s take a look at the four for loops.

Simple for loop

Here’s a look at the most common way to write:

const arr = [1, 2, 3]; for(let i = 0; i< arr.length; i++) { console.log(arr[i]); }Copy the code

When the length of the array does not change during the loop, it is more efficient to store the length of the array as a variable. Here is a modified version:

const arr = [1, 2, 3];
for(let i = 0, len = arr.length; i < len; i++) {
    console.log(arr[i]);
}Copy the code

for-in

In general, we can use for-in to iterate over the contents of an array as follows:

const arr = [1, 2, 3];
let index;
for(index in arr) {
    console.log("arr[" + index + "] = " + arr[index]);
}Copy the code

In general, the results are as follows:

arr[0] = 1
arr[1] = 2
arr[2] = 3Copy the code

But this often leads to problems.

For in truth

A for-in loop iterates over the properties of the object, not the index of the array. Thus, for-in traverses not only arrays, but also objects. Examples are as follows:

const person = {
    fname: "san",
    lname: "zhang",
    age: 99
};
let info;
for(info in person) {
    console.log("person[" + info + "] = " + person[info]);
}Copy the code

The results are as follows:

person[fname] = san
person[lname] = zhang
person[age] = 99Copy the code

Note that the order in which for-in traverses the attributes is not deterministic, that is, the output order is independent of the order in which the attributes are in the object, the alphabetical order of the attributes, or any other order.

The truth of the Array

Array is an object in Javascript, and the index of Array is the property name. In fact, the word “array” in Javascript is a bit misleading, and arrays in Javascript are not like arrays in most other languages. First, arrays in Javascript are not contiguously in memory, and second, an Array’s index is not an offset. In fact, the Array index is not a Number, but a String index. The reason we can write arr[0] correctly is that the language automatically converts the 0 of Number to the 0 of String. So, in Javascript there is never an index to an Array, only attributes like “0”, “1”, and so on. Interestingly, each Array object has a length attribute, causing it to behave more like arrays in other languages. Why is length not printed when traversing an Array object? That’s because for-In can only iterate over “enumerable properties.” Length is a non-enumerable property. In fact, Array objects have many other non-enumerable properties.

Now, let’s go back to the for-in example of looping through arrays. Let’s modify the previous example of looping through arrays:

const arr = [1, 2, 3];
arr.name = "Hello world";
let index;
for(index in arr) {
    console.log("arr[" + index + "] = " + arr[index]);
}Copy the code

The results are as follows:

arr[0] = 1
arr[1] = 2
arr[2] = 3
arr[name] = Hello worldCopy the code

We see a for-in loop that accesses our new “name” attribute, because for-in traverses all of the object’s attributes, not just the “index.” It is also important to note that the index values printed here, i.e. 0, 1, and 2, are not of type Number, but of type String, because they are printed as attributes, not indexes. Does that mean that without adding new properties to our Array object, we can just output the contents of the Array? The answer is no. Because for-In not only iterates through the array’s own properties, it also iterates through all the enumerable properties on the array prototype chain. Here’s an example:

Array.prototype.fatherName = "Father";
const arr = [1, 2, 3];
arr.name = "Hello world";
let index;
for(index in arr) {
    console.log("arr[" + index + "] = " + arr[index]);
}Copy the code

The results are as follows:

arr[0] = 1
arr[1] = 2
arr[2] = 3
arr[name] = Hello world
arr[fatherName] = FatherCopy the code

We can see that a for-in is not suitable for traversing elements in an Array, but rather for traversing properties in an object, which is why it was created. One exception is sparse arrays. Consider the following example:

let key; const arr = []; arr[0] = "a"; arr[100] = "b"; arr[10000] = "c"; for(key in arr) { if(arr.hasOwnProperty(key) && /^0$|^[1-9]\d*$/.test(key) && key <= 4294967294 ) { console.log(arr[key]); }}Copy the code

For-in only iterates through existing entities. In the example above, for-in iterates three times (iterating over elements with attributes “0”, “100”, and “10000”, as opposed to 10001 times for a normal for loop). So, for-ins can be great for traversing Array elements, if done right.

To avoid duplication, we can wrap the code above:

function arrayHasOwnIndex(array, prop) { return array.hasOwnProperty(prop) && /^0$|^[1-9]\d*$/.test(prop) && prop <= 4294967294; // 2^ 32-2}Copy the code

The following is an example:

for (let key in arr) { if (arrayHasOwnIndex(arr, key)) { console.log(arr[key]); }}Copy the code

The for – in performance

As mentioned above, each iteration searches for instance or stereotype attributes simultaneously, and each iteration of a for-in loop incurs more overhead and is therefore slower than other loop types, typically 1/7 of the speed of other loop types. Therefore, you should avoid for-in loops unless you explicitly need to iterate over an object with an unknown number of attributes. If you need to iterate over a limited list of known attributes, it is faster to use other loops, such as the following example:

const obj = {
    "prop1": "value1",
    "prop2": "value2"
};

const props = ["prop1", "prop2"];
for(let i = 0; i < props.length; i++) {
    console.log(obj[props[i]]);
}Copy the code

In the above code, all the attributes of the object are stored in an array. Instead of looking up each attribute for a for-in, the code only focuses on the given attribute, saving the overhead and time of the loop.

forEach

In ES5, a new loop was introduced, the forEach loop.

const arr = [1, 2, 3];
arr.forEach((data) => {
    console.log(data);
});Copy the code

Running results:

1
2
3Copy the code

The forEach method performs a callback forEach item in the array that has a valid value. Items that have been deleted (using the delete method, for example) or that have never been assigned a value are skipped (excluding those whose value is undefined or null). The callback function is passed three arguments in turn:

  • The value of the current item in the array;
  • The index of the current item in the array;
  • The array object itself;

Note that the scope of forEach traversal is determined before the first callback is called. Items added to the array after a call to forEach are not accessed by the callback. If an existing value is changed, the value passed to the callback is the value that forEach traversed up to their moment. Deleted items are not traversed.

const arr = [];
arr[0] = "a";
arr[3] = "b";
arr[10] = "c";
arr.name = "Hello world";
arr.forEach((data, index, array) => {
    console.log(data, index, array);
});Copy the code

Running results:

a 0 ["a", 3: "b", 10: "c", name: "Hello world"]
b 3 ["a", 3: "b", 10: "c", name: "Hello world"]
c 10 ["a", 3: "b", 10: "c", name: "Hello world"]Copy the code

Here the index is of type Number and does not iterate through attributes on the prototype chain like a for-in.

So, with forEach, we don’t need to specifically declare index and iterated elements, because those are used as arguments to the callback function.

In addition, forEach will iterate over all the elements in a number group, but ES5 defines some other useful methods, some of which are as follows:

  • Every: The loop returns after the first return false
  • Some: The loop returns after the first return true
  • Filter: Returns a new array whose elements satisfy the callback function
  • Map: Returns the elements in the original array after processing
  • Reduce: Processes the elements in the array one by one, takes the result of the last processing as the input of the next processing, and finally obtains the final result.

The forEach performance

First of all, I would like to thank @Papa PA for reminding me that I made a mistake in my previous understanding.

If you look at jsPerf, forEach is not as fast as for when tested in different browsers. If you put your test code on the console, you might get different results, mainly because the console execution environment is different from the real code execution environment.

for-of

Let’s start with an example:

const arr = ['a', 'b', 'c'];
for(let data of arr) {
    console.log(data);
}Copy the code

The results are as follows:

a
b
cCopy the code

Why was for-of introduced?

To answer this question, let’s look at the shortcomings of the three for loops prior to ES6:

  • ForEach cannot break or return;
  • The disadvantage of for-In is that it not only iterates through the elements in the array, but also traverses custom attributes, and even attributes on the prototype chain are accessed. Furthermore, the order in which the elements of the traversal array are traversed can be random.

So, in view of these shortcomings, we need to improve the original for loop. But ES6 doesn’t break the JS code you’ve already written. Today, thousands of Web sites rely on for-in loops, and some even use them for array traversal. Adding array traversal support by fixing for-In loops would have made things even messier, so the standards committee added a new loop syntax in ES6 to solve the current problem, for-of.

So what exactly can for-of do?

  • In contrast to forEach, it responds correctly to break, continue, and return.
  • For-of loops support not only arrays, but also most array-like objects, such as DOM Nodelist objects.
  • The for-of loop also supports string traversal, which traverses strings as a series of Unicode characters.
  • For-of also supports traversal of Map and Set objects (both new types in ES6).

To summarize, for-of loops have the following characteristics:

  • This is the simplest and most straightforward syntax for traversing a list of elements.
  • This approach avoids all the pitfalls of a for-in loop.
  • Unlike forEach, it responds correctly to break, continue, and return statements.
  • It can iterate not only over arrays, but also over array-like objects and other iterable objects.

Note that for-of loops don’t support normal objects, but if you want to iterate over an object’s properties, you can use for-in loops (which is what it does).

Finally, ES6 introduces another way to iterate over a set of values: Iterator. Previous example:

const arr = ['a', 'b', 'c'];
const iter = arr[Symbol.iterator]();

iter.next() // { value: 'a', done: false }
iter.next() // { value: 'b', done: false }
iter.next() // { value: 'c', done: false }
iter.next() // { value: undefined, done: true }Copy the code

However, this content is beyond the scope of this article, and there is a lot to be said about Iterator.