Categories
JavaScript Best Practices

JavaScript Best Practices — Things to Avoid

Spread the love

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.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *