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 easily to make our JavaScript code easy to read.
In this article, we look at things to avoid, including declaring global variables whenever possible, passing strings to setInterval
and setTimeout
, the with
statement, and more.
Avoid Declaring Global Variables
We should avoid the use of global variables as much as possible for various reasons.
One is that they’re easy to overwrite in different places since they’re available everywhere. They can also overwrite things in the window
object since global variables are properties of the window
object.
These two are real issues that make our code hard to follow. Therefore, we should define local variables as much as possible.
We can define local variables by using the var
, let
, or const
keywords.
Variables defined with var
are available at the level where it’s defined and below before it’s defined. For example, if we write:
const log = () => {
console.log(x);
}
log();
var x = 1;
log();
Then we get undefined
for the first console.log
and one for the second log.
It’s the same as writing:
Variables declared with let
are available only after they’re defined, so if we give:
const log = () => {
console.log(x);
}
log();
let x = 1;
log();
We get the error:
Uncaught ReferenceError: Cannot access ‘x’ before initialization
With the const
keyword, we define constants that can only be assigned once and never again. It’s also available only after it’s declared unlike var
.
For example, we can write:
const log = () => {
console.log(x);
}
const x = 1;
log();
Calling log
before const x = 1
will also get us Uncaught ReferenceError: Cannot access ‘x’ before initialization
.
If we want variables that are available in different parts of a program, we should use JavaScript modules and then build the modules into one or several large files when we release our code. This has been available since ES6.
We can export
our variables and import
them into other modules. There’s also export default
to export the whole module. This way, we only export things that should be available outside the module and keep everything else private.
We can also use closures to keep variables inside a function so they can’t be accessed outside. An example of a simple closure would be:
const divide = () => {
const x = 3;
return () => x * 2;
}
We keep x
inside the divide
function so that it can’t be accessed outside and return a function that does something with it.
Then we call it by writing:
console.log(divide()());
Photo by Matt Jones on Unsplash
Never Pass a String to setInterval or setTimeout
We should never pass strings to the first argument of the setInterval
or setTimeout
functions instead of a callback function.
If we pass a string into it instead of a callback function, the browser will use eval
to run the code inside the string, which is slow and it’s vulnerable to code inject attacks.
Therefore, there’s no reason unless we really need to run code that’s generated on the fly, which should be very, very rare.
Instead of writing:
setTimeout(
"document.getElementById('foo').textContent = 'foo'", 1000
);
We should write:
setTimeout(() => {
document.getElementById('foo').textContent = 'foo';
}, 1000);
Don’t Use the “with” Statement
On the surface, the with
statement may seem like a good shortcut for accessing deeply nested properties of objects. For example, we can write:
Instead of:
However, it pollutes the global scope and creates ambiguity of whether baz
is accessible outside the with
statement. This is because we can also declare new variables inside the with
statement:
We declared baz
inside with
, but it isn’t accessible outside.
Fortunately, it’s not allowed to be used in JavaScript strict mode so we don’t have to worry about using the with
statement now.
var Keyword
With ES6, we have the block-scoped let
and const
keywords for declaring variables and constants respectively. We can use them to declare variables since they have a clear scope.
Variables declared with var
has a confusing scope. It’s available outside of blocks when it’s declared in a block like an if
block or a loop.
Something like:
for (let i = 0; i < 10; i++){
var j = i;
}
console.log(j);
Works with var
, but we would get an error if we replace var
with let
.
Because the scope is confusing, we should stop using var
to declare variables.
Conclusion
When we write JavaScript, we want to avoid many older constructs that existed in early versions of JavaScript. They make our code hard to read and it’s easy to create bugs with it.
First, we want to avoid global variable declarations to avoid referencing and declaring things accidentally in the global scope. It also reduces the chance of modifying a global variable’s values accidentally.
We also should stop passing in code in a string instead of passing in a callback function to the first argument of the setTimeout
and setInterval
functions. This is because eval
will be used to run the code stored in a string, which exposes our code to code injection attacks and it’s also slow.
Also, we should avoid the with
statement because it creates variables in the global scope.
Likewise, because of the confusing scope of variables declared with the var
keyword, we should avoid declaring variables with the var
keyword.