All ways to iterate over an array in JavaScript

Original author: TJ Crowder
  • Transfer

Content:


  • I. Enumeration of real arrays
    1. ForEach method and related methods
    2. For loop
    3. Proper use of for ... in loop
    4. The for ... of loop (implicit use of an iterator)
    5. Explicit use of an iterator

  • II. Iterating over array-like objects
    1. Using methods to iterate over real arrays
    2. Convert to a Real Array
    3. Note on runtime objects



I. Enumeration of real arrays


There are currently three ways to iterate over the elements of a real array:
  1. method Array.prototype.forEach;
  2. classic cycle for;
  3. "Correctly" constructed cycle for...in.

In addition, soon, with the advent of the new ECMAScript 6 (ES 6) standard, two more ways are expected:
  1. loop for...of(implicit use of an iterator);
  2. explicit use of an iterator.

1. forEach method and related methods


If your project is designed to support the capabilities of the ECMAScript 5 (ES5) standard, you can use one of its innovations - the forEach method .

Usage example:
var a = ["a", "b", "c"];
a.forEach(function(entry) {
    console.log(entry);
});

In general, use forEachrequires the connection of the es5-shim emulation library for browsers that do not have native support for this method. These include IE 8 and earlier, which are still in use here and there.

The advantages forEachinclude the fact that there is no need to declare local variables for storing the index and value of the current element of the array, since they are automatically transferred to the callback function (callback) as arguments.

If you are worried about the possible cost of calling a callback for each item, don't worry and read this .

forEach It is designed to iterate over all elements of the array, but in addition to it, ES5 offers several more useful methods for iterating over all or some elements plus performing any actions with them:
  • every- returns trueif for each element of the array the callback returns the value reducible to true.
  • some- returns trueif for at least one element of the array the callback returns the value reducible to true.
  • filter- creates a new array, including those elements of the original array for which the callback returns true.
  • map - creates a new array consisting of values ​​returned by the callback.
  • reduce - reduces the array to a single value, applying the callback in turn to each element of the array, starting from the first (it can be useful for calculating the sum of the elements of the array and other resulting functions).
  • reduceRight - works similarly to reduce, but iterates over the elements in reverse order.

2. The for loop


Good old forrules :

var a = ["a", "b", "c"];
var index;
for (index = 0; index < a.length; ++index) {
    console.log(a[index]);
}

If the length of the array is unchanged throughout the entire cycle, and the cycle itself belongs to a code segment critical in terms of performance (which is unlikely), then you can use the “more optimal” version forwith the storage of the length of the array:

var a = ["a", "b", "c"];
var index, len;
for (index = 0, len = a.length; index < len; ++index) {
    console.log(a[index]);
}

Theoretically, this code should run a little faster than the previous one.

If the order of enumerating elements is not important, then you can go even further in terms of optimization and get rid of the variable for storing the length of the array, changing the order of enumeration to the opposite:

var a = ["a", "b", "c"];
var index;
for (index = a.length - 1; index >= 0; --index) {
    console.log(a[index]);
}

However, in modern JavaScript engines such optimized games usually mean nothing.

3. Proper use of the for ... in loop


If you are advised to use a loop for...in, remember that enumerating arrays is not what it is intended for . Contrary to a common misconception, the loop for...indoes not iterate over the array indices, but enumerated properties of the object.

However, in some cases, such as enumerating sparse arrays , it for...incan be useful if precautionary measures are taken, as shown in the example below:

// a - разреженный массив
var a = [];
a[0] = "a";
a[10] = "b";
a[10000] = "c";
for (var key in a) {
    if (a.hasOwnProperty(key) &&
        /^0$|^[1-9]\d*$/.test(key) &&
        key <= 4294967294) {
        console.log(a[key]);
    }
}

In this example, at each iteration of the loop, two checks are performed:
  1. that the array has its own property with a name key(not inherited from its prototype).
  2. that keyis a string containing a decimal notation of an integer whose value is less 4294967294. Where does the last number come from? From the definition of the array index in ES5, from which it follows that the highest index, which may be an element in the array: (2^32 - 2) = 4294967294.

Of course, such checks will take extra time during the execution of the cycle. But in the case of a sparse array, this method is more efficient than a loop for, since in this case only those elements that are explicitly defined in the array are sorted. So, in the example above, only 3 iterations will be performed (for indices 0, 10 and 10000) - versus 10001 in the loop for.

In order not to write such a cumbersome verification code every time when iterating through an array is required, you can design it as a separate function:

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

Then the body of the cycle from the example will be significantly reduced:

for (key in a) {
    if (arrayHasOwnIndex(a, key)) {
        console.log(a[key]);
    }
}

The verification code discussed above is universal, suitable for all cases. But instead, you can use a shorter version, although not formally correct, but nonetheless suitable for most cases:

for (key in a) {
    if (a.hasOwnProperty(key) && String(parseInt(key, 10)) === key) {
        console.log(a[key]);
    }
}

4. The for ... of loop (implicit use of an iterator)


ES6, while still in draft status , should introduce iterators in JavaScript.

An iterator is a protocol implemented by an object that defines a standard way to obtain a sequence of values ​​(finite or infinite).
An iterator is an object in which a method next()is defined - a function without arguments that returns an object with two properties:
  1. done( boolean) - takes on value trueif the iterator reaches the end of the iterable sequence. Otherwise, it matters false.
  2. value- defines the value returned by the iterator. It may not be defined (absent) if the property donematters true.

Many built-in objects, including real arrays have default iterators. The simplest way to use an iterator in real arrays is to use a new design for...of.

Usage example for...of:

var val;
var a = ["a", "b", "c"];
for (val of a) {
    console.log(val);
}

In the above example, the loop for...ofimplicitly calls the iterator of the Array object to get each value of the array.

5. Explicit use of an iterator


Iterators can also be used explicitly, however, in this case, the code becomes much more complicated compared to a loop for...of. It looks something like this:

var a = ["a", "b", "c"];
var it = a.entries();
var entry;
while (!(entry = it.next()).done) {
    console.log(entry.value[1]);
}

In this example, the method Array.prototype.entriesreturns an iterator, which is used to output the values ​​of the array. At each iteration, entry.valuecontains an array of view [ключ, значение].

II. Iterating over array-like objects


In addition to real arrays, in JavaScript there are also array-like objects . With real arrays, they are related by the fact that they have a property lengthand properties with names in the form of numbers corresponding to the elements of the array. Examples are the DOM collections NodeListand the pseudo-array argumentsaccessible inside any function / method.

1. Using methods of enumerating real arrays


At least most, if not all, methods of enumerating real arrays can be used to enumerate array-like objects.

Constructs forand for...incan be applied to array-like objects in exactly the same way as to real arrays.

forEachand other methods are Array.prototypealso applicable to array-like objects. To do this, use the call to Function.call or Function.apply .

For example, if you want to apply forEachto an childNodesobject property Node, then this is done like this:

Array.prototype.forEach.call(node.childNodes, function(child) {
    // делаем что-нибудь с объектом child
});

For the convenience of reusing this technique, you can declare a reference to the method Array.prototype.forEachin a separate variable and use it as a shorthand:

// (Предполагается, что весь код ниже находится в одной области видимости)
var forEach = Array.prototype.forEach;
// ...
forEach.call(node.childNodes, function(child) {
    // делаем что-нибудь с объектом child
});

If the array-like object has an iterator, then it can be used explicitly or implicitly to iterate over the object in the same way as for real arrays.

2. Convert to a real array


There is also another, very simple, way to iterate over an array-like object: convert it to a real array and use any of the above methods for iterating over real arrays. For conversion, you can use the universal method Array.prototype.slice, which can be applied to any array-like object. This is done very simply, as shown in the example below:

var trueArray = Array.prototype.slice.call(arrayLikeObject, 0);

For example, if you want to convert a collection NodeListinto a real array, you need something like this code:

var divs = Array.prototype.slice.call(document.querySelectorAll("div"), 0);

Update : As rock and torbasow noted in ES6 , Array.prototype.sliceyou can use a more intuitive method instead of ES6 Array.from.

3. Note on runtime objects


If you apply methods Array.prototypeto runtime objects (such as DOM collections), then you should keep in mind that the correct operation of these methods is not guaranteed in all runtime environments (including browsers). It depends on the behavior of a particular object in a particular runtime, more precisely, on how the abstract operation is implemented in this object HasProperty. The problem is that the ES5 standard itself allows the possibility of incorrect behavior of the object with respect to this operation (see §8.6.2 ).

Therefore, it is important to test the operation of methods Array.prototypein each runtime (browser) in which you plan to use your application.

Also popular now: