JavaScript lets us do a lot of things. It’s sometimes too forgiving in its syntax.
In this article, we’ll look at some antipatterns that we should avoid when we write JavaScript loops.
for Loops
for loops are handy for iterating over arrays and array-like objects.
For instance, we can write:
const arr = [1, 2, 3];
for (let i = 0; i < arr.length; i++) {
// do something with arr[i]
}
This loops the indexes of the arr
array and let us do something with each entry.
It also works with array-like objects. Nodelists are array-like objects.
We can call the following methods to get a Nodelist with multiple DOM elements:
document.querySelectorAll()
document.getElementsByName()
document.getElementsByClassName()
document.getElementsByTagName()
document.querySelectorAll()
is the most versatile since it accepts any CSS selector.
document.getElementsByName
only returns the items with the given name attribute.
document.getElementsByClassName
only returns the items with the given class name.
document.getElementsByTagName
returns items with the given tag name.
Also, the following properties of the document have specific items
document.images
— allimg
elements on a pagedocument.links
— alla
elementsdocument.forms
— all formsdocument.forms[0].elements
— all fields in a form
We can loop through them as follows:
for (let i = 0; i < document.links.length; i++) {
// do something with document.links[i]
}
With the for
loop, we can define all the initial conditions in the first expression, so we can write:
for (let i = 0, max = arr.length; i < max; i++) {
// do something with arr[i]
}
which is the same as:
for (let i = 0; i < arr.length; i++) {
// do something with arr[i]
}
i++
is the same as i = i + 1
or i += 1
.
i++
can be tricky if we assign it to something.
i++
returns the original value of i
instead of the updated value.
So if we have:
let i = 1;
const a = i++;
We get that a
is 1.
for-in Loops
for-in loops are useful for looping through keys of an object and its prototypes.
Therefore, if we only want to loop through the object’s non-inherited keys, we’ve to use the hasOwnProperty
method.
For instance, we can use it by writing:
const obj = {
a: 1,
b: 2
}
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
//...
}
}
That will only loop through the keys that are non-inherited as hasOwnProperty
checks that.
We can also call hasOwnProperty
as follows:
const obj = {
a: 1,
b: 2
}
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
//...
}
}
This will avoid issues when the obj
object redefined hasOwnProperty
to be something else.
We can also write:
const obj = {
a: 1,
b: 2
}
const hasOwn = Object.prototype.hasOwnProperty;
for (const key in obj) {
if (hasOwn.call(obj, key)) {
//...
}
}
to cache the hasOwnProperty
method.
for-of Loop
The for-of loop is more versatile than the other loops. It’s useful for looping through the arrays and array-like objects.
So it’s a great alternative to the for loop for looping through those objects.
It also works with all the document
properties listed above and also new data structures like sets and maps.
We can use it as follows:
for (const link of document.links) {
// do something with link
}
For arrays, we can write:
for (const a of arr) {
// do something with a
}
It also works with the destructuring assignment syntax:
for (const { foo, bar } of arr) {
// do something with foo and bar
}
It also works great with sets and maps:
for (const s of set) {
// do something with a
}
For maps, we can write:
const map = new Map([
['a', 1],
['b', 2]
])
for (const [key, value] of map) {
// do something with key and value
}
Conclusion
A for loop is great for looping through arrays and array-like objects.
However, the for-loop beats that by letting us loop through any iterable object.
The for-in loop has limited uses for looping through keys in an object. If we want to loop through their prototype’s keys as well, then we can use that.