All ways to iterate over an array in JavaScript
- Transfer
Content:
- I. Enumeration of real arrays
- ForEach method and related methods
- For loop
- Proper use of for ... in loop
- The for ... of loop (implicit use of an iterator)
- Explicit use of an iterator
- II. Iterating over array-like objects
- Using methods to iterate over real arrays
- Convert to a Real Array
- Note on runtime objects
I. Enumeration of real arrays
There are currently three ways to iterate over the elements of a real array:
- method
Array.prototype.forEach
; - classic cycle
for
; - "Correctly" constructed cycle
for...in
.
In addition, soon, with the advent of the new ECMAScript 6 (ES 6) standard, two more ways are expected:
- loop
for...of
(implicit use of an iterator); - 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
forEach
requires 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
forEach
include 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
- returnstrue
if for each element of the array the callback returns the value reducible totrue
.some
- returnstrue
if for at least one element of the array the callback returns the value reducible totrue
.filter
- creates a new array, including those elements of the original array for which the callback returnstrue
.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
for
rules :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
for
with 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...in
does not iterate over the array indices, but enumerated properties of the object. However, in some cases, such as enumerating sparse arrays , it
for...in
can 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:
- that the array has its own property with a name
key
(not inherited from its prototype). - that
key
is a string containing a decimal notation of an integer whose value is less4294967294
. 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:done
(boolean
) - takes on valuetrue
if the iterator reaches the end of the iterable sequence. Otherwise, it mattersfalse
.value
- defines the value returned by the iterator. It may not be defined (absent) if the propertydone
matterstrue
.
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...of
implicitly 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.entries
returns an iterator, which is used to output the values of the array. At each iteration, entry.value
contains 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
length
and properties with names in the form of numbers corresponding to the elements of the array. Examples are the DOM collections NodeList
and the pseudo-array arguments
accessible 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
for
and for...in
can be applied to array-like objects in exactly the same way as to real arrays. forEach
and other methods are Array.prototype
also applicable to array-like objects. To do this, use the call to Function.call or Function.apply . For example, if you want to apply
forEach
to an childNodes
object 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.forEach
in 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
NodeList
into 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.slice
you can use a more intuitive method instead of ES6 Array.from
.3. Note on runtime objects
If you apply methods
Array.prototype
to 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.prototype
in each runtime (browser) in which you plan to use your application.