JavaScript is a very forgiving language. It’s easy to write code that runs but has mistakes in it.
In this article, we’ll look at some JavaScript best practices to follow, including avoiding empty function and destructuring, null comparison, avoiding useless bind
and avoid eval
.
Don’t Write Empty Functions
Empty functions aren’t useful and we don’t know if they’re intentional or not. This means that we should at least write a comment to let people know whether it’s intentional.
This also goes for arrow function callbacks that return nothing, since they look similar to an empty object.
For instance, we should avoid:
function foo() {}
arr.map(() => {});
arr.map(() => ({}));
Instead, we should write:
function foo() {
// do nothing
}
or:
arr.map(() => {}); // returns nothing
or:
arr.map(() => ({})); // returns empty object
Empty Destructuring Patterns
Empty destructuring patterns are also useless, so there’s no point in creating them. For instance, the following isn’t useful:
const {a: {}} = foo;
It might also be mistaken for setting a
to an empty object as the default value as follows:
const {a = {}} = foo;
We should avoid having empty destructuring patterns in our code. If we have any, we can and should remove them.
Null Comparisons with ==
null
comparisons with ==
or !=
also compares the operand we’re comparing with other values with undefined
. Therefore, the following are also true
if we compare foo
with null
in the following code:
let foo = undefined;
foo == null;
We get that foo == null
returns true
, which is probably not what we want. Likewise, if we have the following comparison with !=
:
let foo = undefined;
foo != null;
We get that foo != null
is false
even though foo
is undefined
. Therefore, we should instead use ===
and !==
to check for null
as follows:
let foo = undefined;
foo === null;
and:
let foo = undefined;
foo !== null;
Never Use eval()
eval
lets us pass in a string with JavaScript code and run it. This is dangerous because it can potentially let anyone run JavaScript code that we didn’t write. It can open up our program to several kinds of injection attacks.
No Extension of Native Objects
In JavaScript, we can extend any native objects with their own methods. However, that’s not a good idea because it may break other pieces of code that use native objects in ways that we don’t expect.
For instance, if we have the following:
Object.prototype.foo = 55;
const obj = {
a: 1,
b: 2
};
for (const id in obj) {
console.log(id);
}
We see that foo
is logged in addition to a
and b
because the for...in
loop iterates through all enumerable properties in the current object and the object’s prototype chain.
Since JavaScript extends the JavaScriptObject
object by default, the for...in
loop will also loop through enumerable properties in the Object
’s prototype.
Therefore, we shouldn’t extend native objects as it may do things that we don’t expect to other code.
No Unnecessary Function Binding
The bind
method is a method of a function that changes the value of this
inside a traditional function. Therefore, if our function doesn’t reference this
, then we don’t need to call bind
on it to change the value of this
.
For example, the following function uses bind
with a purpose:
function getName() {
return this.name;
}
const name = getName.bind({
name: "foo"
});
console.log(name());
In the code above, we have the getName
function, then we called bind
on it to change the value of this
to { name: “foo” }
. Then we when we call name
in the last line, we see return this.name
, which should be 'foo'
since we changed the value of this
to the object.
On the other hand, if we have:
function getName() {
return 'foo';
}
const name = getName.bind({
name: "foo"
});
console.log(name());
Then the call to bind
is useless since getName
didn’t reference this
. Therefore, if our function doesn’t reference this
, then we don’t need to call bind
on it.
Conclusion
Comparing null
with ==
isn’t very useful since expressions like undefined == null
also return true
because of type coercion. To ensure attackers can’t run malicious code, eval
shouldn’t be called.
bind
is useless if we don’t reference this
in our function, so if we do call it, we should make sure that we have this
in our function. Other things like empty functions and destructuring patterns are useless, so they should be avoided.