Categories
JavaScript Best Practices

JavaScript Best Practices – Returns, scopes, and if statements

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 returning values in certain callbacks, using block-scoped variables, cleaning up the if statements, and returning clearly.


Returning Values in Callbacks Passed Into Array Methods

Many JavaScript array methods, like map, from, every, some, reduce, find, findIndex, reduceRight, filter, and sort, take a callback.

The callbacks that are passed in should return some value so that these methods can return a proper value, allowing us to do other things. For instance, the following code is probably useless and a mistake:

const arr = [1, 2, 3];
const mapped = arr.map(() => {});

Then we see that the value of mapped is [undefined, undefined, undefined]. It’s probably not what we want. Therefore, we should make sure that the callbacks that these array methods take return a value so that we don’t get something unexpected.

We shouldn’t have callbacks that don’t return any value unless we’re 100% sure that we want to do this.


Use Block-Scoped Variables

If we use var to declare variables, we should treat them as if they’re block-scoped so that we don’t cause confusion for ourselves and other developers.

For instance, we can write something like this:

foo logs true as we expected, but the confusion is that we declared var foo twice in one if block.

We should treat var like a block-scoped variable to reduce confusion, so we should instead write:

Better yet, we should use let for block-scoped variables:

let is always block-scoped so that we can’t put the variables anywhere we want like with var.


Class Method Should Reference this

Instance methods should reference this in a JavaScript class. Otherwise, there’s no point in it being an instance method. If we don’t need to reference this in a class method, then it should be a static method.

For instance, if we have:

The bar shouldn’t be an instance method since it doesn’t reference this . Instead, we should make it a static method as follows:


Limiting Linearly Independent Paths Through a Piece of Code

We shouldn’t have too many else if blocks in any if statement to reduce complexity. To make code easy to read, we should reduce the number of paths that an if statement can take.

For instance, we can write:

We should consider reducing the cases of if statements.

Photo by Rod Long on Unsplash.


Return Statements Should Specify a Value or Not Specify a Value

In JavaScript, the return statement can return undefined in multiple ways. return with nothing after it returns undefined. return with void after it and expression after void also returns undefined.

return with undefined after it also returns undefined.

We should consider returning with nothing or returning undefined or an expression explicitly to make what we’re trying to return or not return clear.

So we should either write:

const bar = () => {
  return;
}

or:

const bar = () => {
  return 1;
}

or:

const bar = () => {
  return undefined;
}

The void operator is rarely used, so most developers aren’t familiar with it. Its use is also limited. Therefore, it’s not used often.


Conclusion

Many array methods take a callback that should return a value. Instance methods like map, reduce, filter, etc. all have callbacks that should return values.

Class instance methods should reference this. If they don’t, then they should be static methods.

if statements shouldn’t be too complex. Keep the number of paths to a minimum.

Finally, return should return values or nothing rather than using the void operator with it.

Categories
JavaScript Best Practices

JavaScript Best Practice — Replacing Old with New

Like any other programming language, JavaScript has its own list of best practices to make programs easier to read and maintain. There are a lot of tricky parts to JavaScript, and we can follow some best practices to improve our code.

Since ES6 was introduced, new constructs are replacing older ones for good reasons. It’s much shorter, cleaner, and easier to understand.

In this article, we’ll look at which older constructs that can be replaced with new ones, including replacing then with async and await, replacing dictionary objects with Maps, replacing apply with the spread operator, and replacing function constructors with the class syntax.

Replace Then with Async / Await

When we chain promises, we used to do it by using the then method and then returning another promise in the callback function that we pass into then.

This means that we have code that looks something like this:

Promise.resolve(1)
  .then((val) => {
    console.log(val);
    return Promise.resolve(2);
  })
  .then((val) => {
    console.log(val);
    return Promise.resolve(3);
  })
  .then((val) => {
    console.log(val);
  })

With the introduction of the async and await syntax, which is just a shorthand for calling the then method repeatedly. We can write the code above like this:

(async () => {
  const val1 = await Promise.resolve(1);
  console.log(val1);
  const val2 = await Promise.resolve(2);
  console.log(val2);
  const val3 = await Promise.resolve(3);
  console.log(val3);
})();

They both output 1, 2 and 3, but the second code snippet is so much shorter. It’s so much clearer that there’s no reason to go back to the old syntax.

Also, we can loop through promises and run them one after the other by using the for-await-of loop. We can loop through the promises we have above by rewriting the code like the following:

(async () => {
  const promises = [
    Promise.resolve(1),
    Promise.resolve(2),
    Promise.resolve(3),
  ]

  for await (let p of promises) {
    const val = await p;
    console.log(val);
  }
})();

Code like the one we have above is very handy for looping through many promises or promises that are created on the fly, which we can’t do in the earlier examples.

Replacing Dictionary Objects with Maps

ES6 also introduced the Map constructor, which lets us create hash maps or dictionaries without using JavaScript objects with string keys to do so.

Maps are also better because they have their own methods to get and manipulate the entries.

For example, instead of writing:

const dict = {
  'foo': 1,
  'bar': 2
}

We write:

const dict = new Map([
  ['foo', 1],
  ['bar', 2]
])

Then we can get an entry by using the get method as follows:

console.log(dict.get('foo'));

We can set the value of an existing entry by the key of the entry with the set method:

dict.set('foo', 2);

Also, we can check if an entry exists with the given key with the has method:

dict.has('baz');

There are also the keys and entries methods to get all the keys of the map and all the entries respectively.

For example, we can write:

console.log(dict.keys());

To get an iterator object with the keys of the Map . This means that we can loop through them with the for...of loop or convert it to an array with the spread operator.

Similarly, the entries method returns an iterator object with all the entries in the Map with each entry being an array of [key, value] .

There’s also the value method to get an iterator object with all the values in the Map .

We can also use other primitive values as keys. If we use objects, we can’t get the value back when we look them up since the lookup is done with the === operator which returns false is 2 objects that don’t have the same reference even if they have the same content.

These methods aren’t available in a regular object. Also, we might accidentally get or modify the object’s prototype’s properties instead of its own if we use the for...in loop.

Therefore, there aren’t many reasons to use a regular object as a dictionary anymore.

Photo by David Clode on Unsplash

Replace Apply with Spread

If we don’t want to change the value of this inside the function, there isn’t much reason to use the apply method in functions.

If we only want to call a function with many arguments, we can use the spread operator when passing in an array for our arguments as follows:

const arr = [1, 2, 3, 4, 5];
const add = (...args) => args.reduce((a, b) => a + b, 0);
console.log(add(...arr));

All we have to do is to use the spread operator on our array, which is the 3 dots operator in the last line, and then the array will be separated into a comma-separated list of arguments.

Replace Constructor Functions with Classes

Another great ES6 feature is the class syntax for constructor functions. It’s simply syntactic sugar that makes the constructor function looks like a class.

The advantage of it is that it makes inheritance easy.

For example, if we want to inherit from a constructor function, we have to write something like this:

function Person(name) {
  this.name = name;
}

function Employee(name, employeeCode) {
  Person.call(this, name);
  Employee.prototype.constructor = Person;
  this.employeeCode = employeeCode;
}

const employee = new Employee('Joe', 1);
console.log(employee)

This syntax looks strange coming from class-based object-oriented languages like Java.

However, the class syntax makes things look a lot familiar to developers that used other languages more than JavaScript. We can rewrite the code above to the following:

class Person {
  constructor(name) {
    this.name = name;
  }
}

class Employee extends Person {
  constructor(name, employeecode) {
    super(name);
    this.employeecode = employeecode;
  }
}

const employee = new Employee('Joe', 1);
console.log(employee)

The code does the same thing as what we have above. However, it’s clearer what we’re inheriting from since we have the extends keyword to indicate what we’re inheriting from.

With the constructor function, we have to worry about the value of this in the first argument of the call method, and what we pass into the subsequent arguments of call.

With the class syntax, we don’t have to worry about this. If we forgot to make the super call like the following code:

class Person {
  constructor(name) {
    this.name = name;
  }
}

class Employee extends Person {
  constructor(name, employeecode) {
    this.employeecode = employeecode;
  }
}

const employee = new Employee('Joe', 1);
console.log(employee)

We’ll get the error ‘Uncaught ReferenceError: Must call super constructor in derived class before accessing ‘this’ or returning from derived constructor.’

It’s one less chance of making a mistake.

We don’t get any error if we omit the Person.call like in the Employee constructor function since the browser doesn’t know we want Employee to inherit from Person .

In addition, when we log the prototype of employee , we get that the prototype of employee is Person as expected with the class syntax.

However, we don’t get that unless we put Employee.prototype.constructor = Person; which is easily forgotten.

Conclusion

async and await and for-await-of are new constructs that make chaining promises much cleaner. It’s much better to use them instead of using then because of it.

for-await-of also lets us loop through promises that are generated dynamically which we can’t do with then or async and await alone.

Map s are much better than plain objects for hashes and dictionaries because it has its own methods to manipulate and get the entries. Also, we may accidentally be accessing the properties of prototype of plain objects.

If we don’t want to change the value of this inside a function, we can replace the apply method for calling functions with an array of arguments with the spread operator, which does the same thing.

Finally, the class syntax for constructors is much better than the original function syntax since we can inherit from parent classes easier than setting the prototype constructor of a constructor function.

Categories
JavaScript Best Practices

JavaScript Best Practice — New Constructs and Functions

Like any other programming language, JavaScript has its own list of best practices to make programs easier to read and maintain. There are also lots of tricky parts to JavaScript. We can follow some best practices easily to make our JavaScript code easy to read.

In this article, we’ll look at replacing IIFE with modules and blocks, replacing traditional functions with class methods and arrow functions, removing language attribute from script tags, writing pure functions, and avoiding a long list of arguments.

Replacing IIFE with Modules and Blocks

IIFE stands for Immediately Invoked Function Expression. It’s a construct where we define a function and then call it immediately.

It’s a popular way to isolate data to prevent them from being accessed from, the outside. Also, it hides them from the global scope.

This is handy before ES6 because there were no standards for modules and there’s also no easier way to hide things from the global scope.

However, with ES6, modules are introduced as a new feature. This means that we can replace something like:

(function() {
  let x = 1;
})()

With a module that has:

let x = 1;

If we don’t want it to be available outside the module, we just don’t export it.

With modules, there’s also no need for namespacing with IIFEs since we can put modules in different folders to separate them from each other.

Also, another new feature of ES6 is blocks. Now we can define blocks of code segregated from the outside scope by using curly braces as follows:

{
  let x = 1;
  console.log(x);
}

With block-scope keywords like let or const for variable or constant declaration, we don’t have to worry about things that we don’t want to be accessed outside from being accessed.

Now we can define arbitrary blocks without if statements, loops, or IIFEs.

Replace Traditional Functions with Arrow Functions and Class Methods

Again, with ES6, we have the class syntax for constructor functions. With that, we don’t need the function keyword to create constructor functions.

It makes inheritance much easier and there’s no confusion about this .

In addition, we have arrow functions that don’t change the value of this inside the function.

The only reason left to use the function keyword is for generator functions, which are declared with the function* keyword.

For example, we can create a simple generator function by writing the following:

const generator = function*() {
  yield 1;
  yield 2;
}

Note that generator functions can only return generators so we can’t return anything else from it.

From the Language Attribute From Script Tags

The language attribute no longer has to be included with script tags.

For example, instead of writing:

<script src="[https://code.jquery.com/jquery-2.2.4.min.js](https://code.jquery.com/jquery-2.2.4.min.js)" integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=" crossorigin="anonymous" language='text/javascript'></script>

We write:

<script src="[https://code.jquery.com/jquery-2.2.4.min.js](https://code.jquery.com/jquery-2.2.4.min.js)" integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=" crossorigin="anonymous"></script>

This is because JavaScript is the language left that runs in browsers.

Photo by Ambitious Creative Co. – Rick Barrett on Unsplash

Write Pure Functions

We should write functions as pure functions. That is functions that always gives the same output given that we pass in the same input.

This is important because it makes testing easy. Also, we know what it’ll do exactly, reducing the chance of bugs.

Pure functions are easy to read and understand. The flow is determinate and simple. Therefore, it’s predictable.

An example of a pure function would be the following:

const add = (a, b) => a + b;

The add function always givens the same output if we give it the same inputs since it just computes the results from the parameters. It doesn’t depend on anything else.

Therefore, the logic flow of it is very predictable.

An example of a function that’s not a pure function would be one that returns different output even if the same inputs are given. For example, if we have a function to get the year number that is x years before this year:

const getYearBefore = (x) => new Date().getFullYear() - x;

This wouldn’t be a pure function because new Date() changes according to the current date. So the result of getYearBefore depends on the current date’s year given the same input.

To make it a pure function we can instead write:

const getYearBefore = (date, x) => date.getFullYear() - x;

Since the date is now passed into the function, we get the same results given that we have the same input since nothing in our function is non-deterministic. It’s just combining the parameters together and returning the result.

Also, we don’t have to worry about the current date or how the date implementation changes.

Extending the function is hard before we changed it to a pure function because of the unpredictability of the result of new Date() .

In addition, changing new Date() may break other parts of the program before out change. Now we don’t have to worry about that.

Because of the unpredictability, it’s also harder to trace and debug.

Avoid Long Argument List

With the destructuring syntax of ES6, we can pass in lots of arguments to a function without actually passing them all in separately.

For example, instead of writing:

const add = (a, b, c, d, e) => a + b + c + d + e;

which has 5 parameters. We can write:

const add = ({
  a,
  b,
  c,
  d,
  e
}) => a + b + c + d + e;

This way, we can just pass in one object into the function and get 5 variables inside the add function.

We can call add as follows:

const obj = {
  a: 1,
  b: 2,
  c: 3,
  d: 4,
  e: 5
}

add(obj);

This also works for nested objects. For example, we can write:

const buildString = ({
  foo: {
    bar,
    baz
  }
}) => `${bar} ${baz}`;

Then we can call buildString as follows:

const obj = {
  foo: {
    bar: 'bar',
    baz: 'baz'
  }
};

buildString(obj);

To keep data private, we can use modules and blocks to replace IIFEs. Now we don’t need extra code to define functions and call it.

Also, the class syntax is a much clearer way to define constructors, especially when we want to inherit from other constructors. The value of this is also clearer.

Writing pure functions is a good practice because it’s predictable since we always have the same outputs given the same inputs. It’s also easier to read and test because of it.

With the destructuring syntax, we can reduce lots of arguments to variables in an object. Everything is assigned automatically given the key name and position of the properties we pass in as the argument.

Categories
JavaScript Best Practices

JavaScript Best Practices — Things to Avoid

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.

Categories
JavaScript Best Practices

JavaScript Best Practices: More Things to Avoid

ike 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.