Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62
Subscribe to my email list now at http://jauyeung.net/subscribe/
Like any other programming language, JavaScript has its own list of best practices to make programs easier to read and maintain. There are lots of tricky parts to JavaScript, so there are many things to avoid.
We can follow some best practices to make our JavaScript code easy to read.
In this article, we look at more constructs that we should avoid, including the misuse of the for...in
loop, the ==
operator, the arguments
object, and this
.
Misusing the For…in Loop
The for...in
loop shouldn’t be used to iterate through arrays and array-like objects. For example, if we have:
const arr = ['a', 'b', 'c', 'd', 'e'];
for (let i in arr) {
console.log(i)
}
We get back:
0
1
2
3
4
However, this is a misuse of the for...in
loop since it’s supposed to iterate through the enumerable properties of an object and its prototype rather than looping through the indexes of an array.
The order of the enumeration isn’t guaranteed, so we might not loop through the array in order, which is bad.
The for...in
loop also loops through inherited properties that are enumerable, which is another problem.
For example, if we have:
Then we create an object with a prototype object with the foo
property. The for...in
loop will loop through both the enumerable properties of obj
’s prototype and obj
.
To avoid looping through the properties of its prototype, we can use Object.keys
instead:
for (let prop of Object.keys(obj)) {
console.log(prop);
}
We can loop through the key-value pair of obj
without anything from its prototype, we can use Object.entries
:
for (let entry of Object.entries(obj)) {
console.log(entry);
}
For looping through arrays and array-like objects, we should use the for...of
loop instead:
const arr = ['a', 'b', 'c', 'd', 'e'];
for (let a of arr) {
console.log(a)
}
Then we get the entries of the array.
Messing With this
The this
object is always a problem in JavaScript. This is because it changes depending on the scope.
Fortunately, in ES6, we have arrow functions that don’t change the value of this
if we reference it inside. This also means that we can’t change the value of this
with call
, bind
, or apply
.
For example, the following code will log the window
object:
The value of this
doesn’t change even if we try to change it with bind
.
Also, ES6 has the class syntax for creating constructor functions so it’s clear that this
should be inside the class as long as we use class methods and arrow functions.
Class methods will have the class as the value of this
and arrow functions inside the class will have the same value for this
.
Photo by Casey Allen on Unsplash
The == Operator
The ==
operator does automatic type conversion before comparing the operands for equality. This creates issues with different types of data being considered the same.
For example, null == undefined
returns true
, which we probably don’t want.
null == undefined
returns true
because they’re both falsy.
To avoid these kinds of issues, we should use the ===
operator instead, which would avoid comparisons of these kinds of operands returning true
. This is because ===
checks the type of each operand in addition to the operand’s contents.
The arguments Object
With the introduction of the rest operator, we can finally avoid the use of the arguments
object for getting the arguments that were passed into a function.
The arguments
object shouldn't be used for a few reasons. One is that it’s an array-like object, which means properties like length
are in it and it can be looped through with a for
loop, but array methods like map
and forEach
aren’t included in it.
Also, we can access its entries with its index, which is deceiving since it’s not actually an array.
Using arguments
also prevents any code optimization by browser engines, which means performance is slower. Also, the arguments
object isn’t available in arrow functions.
Therefore, to access extra arguments that are beyond the parameters listed, we should use the rest operator instead.
For example, instead of writing:
We can write:
const add = (...args) => args.reduce((a, b) => a + b, 0);
console.log(add(1, 2, 3, 4, 5));
Notice that it’s much shorter to use the rest operator, which is the ...args
part of the function signature. args
is already an array so we have all the array methods available for us to use.
Conclusion
In modern JavaScript, we can abandon a lot of older constructs to make our code easier to read and maintain.
We can use the rest operator instead of the arguments
object. This is because the rest operator gives us an array of arguments instead of the array-like arguments
object.
Also, we should avoid the ==
operator for equality comparison since it does automatic type conversion before comparing which we might not want.
We should also avoid messing with this
by using arrow functions and class syntax for constructor functions. These two constructs make the value of this
a lot clearer. We only use class methods for classes and arrow functions for functions that aren’t a method of a class.
Finally, the for...in
loop shouldn’t be used for looping through arrays and array-like objects since the order of enumeration isn’t guaranteed to be in order, leading to unexpected results.