Categories
JavaScript Best Practices

JavaScript Best Practices — Exceptions, Loops, and Returns

Like any kind of apps, JavaScript apps also have to be written well. Otherwise, we run into all kinds of issues later on.

In this article, we’ll look at some best practices we should follow when writing JavaScript code.

No Multiple Spaces Inside Regex Literals

We shouldn’t have multiple spaces inside regex literals.

For instance, we shouldn’t write:

const regex = /foo  bar/;

Instead, we write:

const regexp = /foo bar/;

Assignment in return Statements Must be Surrounded by Parentheses

We should surround return statements in parentheses so we can be sure that the assignment is being done before returning the value.

For instance, instead of writing:

function add (a, b) {
  return result = a + b;
}

We write:

function add (a, b) {
  return (result = a + b);
}

Don’t Assign a Variable to Itself

Assigning in a variable to itself is a useless statement, so we shouldn’t write it.

For instance, we shouldn’t write things like:

num = num;

Don’t Compare a Variable to Itself

Since comparing a variable to itself is always true , it’s useless to have a variable compared to itself.

For instance, we shouldn’t write:

if (score === score) {}

Don’t Use the Comma Operator

The comma operator always returns the last expression in the sequence, so it’s pretty useless.

Therefore, we shouldn’t have code like:

const foo = (doSomething(), !!test);

Don’t Assign Things to Restricted Names

We shouldn’t create variables with restricted names and assign values to them.

It doesn’t work and it just causes confusion.

For example, we shouldn’t have code like:

let undefined = 'value';

No Sparse Arrays

We can have arrays that don’t have an entry in even slot.

To do that, we just write commas with nothing in between it to create sparse arrays.

However, this can easily be mistaken for a typo, or that it is a typo.

Therefore, we shouldn’t have expressions like that.

For instance, we shouldn’t write things like:

let fruits = ['banana',, 'grape'];

No Tabs

Tabs cause issues with different text editors.

The spacing from tabs are inconsistent between them,

Therefore, we shouldn’t use them.

Instead, we use 2 spaces or convert tabs to 2 spaces automatically for indentation.

Regular Strings Should Contain Template Literal Placeholders

It’s easy to mistaken template strings and regular strings since they use similar characters as delimiters.

We can’t have template literal placeholders in regular strings.

Therefore, we shouldn’t have code like:

const greeting = 'hi  ${name}';

Instead, we write:

const greeting = `hi  ${name}`;

Template strings are delimited with backticks rather than single quotes.

super Must be Called Before this

In class constructors, we must call super before this .

Otherwise, we should get an error.

Therefore, instead of writing:

class Dog extends Animal {
  constructor () {
    this.breed = 'chow chow';
    super();
  }
}

We write:

class Dog extends Animal {
  constructor () {
    super();
    this.breed = 'chow chow';
  }
}

Always Throw Error Instances

An Error instance has useful information like stack traces and line number where the error is.

Therefore, we should always throw Error instances instead of other values.

So we shouldn’t write:

throw 'error';

But we should write:

throw new Error('error');

Whitespaces Shouldn’t at the End of a Line

Whitespaces shouldn’t be at the end of a line.

They don’t do much. So we should remove them.

Don’t Initialize to undefined

We shouldn’t initialize a variable to undefined .

A variable is already undefined when we declare them without value.

So we don’t need to set it to undefined explicitly.

For instance, instead of writing:

let name = undefined;

We just write:

let name;

No Unmodified Conditions of Loops

If we forget to update the condition, then we’ll probably get an infinite loop.

Therefore, we should remember to modify the condition in each iteration.

For instance, instead of writing:

for (let i = 0; i < arr.length; j++) {...}

We write:

for (let i = 0; i < arr.length; i++) {...}

Conclusion

We shouldn’t have loops that have conditions that don’t update.

Also, we shouldn’t have useless spaces in our code.

Throwing Error instance is better than throwing other values since it provides more information.

Categories
JavaScript Best Practices

JavaScript Best Practices — Spaces, Objects, and Strings

Like any kind of apps, JavaScript apps also have to be written well.

Otherwise, we run into all kinds of issues later on.

In this article, we’ll look at some best practices we should follow when writing JavaScript code.

No Mixing Spaces and Tabs for Indentation

Mixing spaces and tab for cause issues for text editors.

Therefore, we shouldn’t mix them.

Instead, we use 2 spaces and convert tabs to 2 spaces automatically.

No Multiple Spaces Except for Indentation

We only use 2 spaces for indentation.

In all other locations, we should use one space to avoid wasting space.

No new Without Assigning the Return Object to a Variable

If we create a new object with new , then we should assign it to a variable so we can use it.

For instance, instead of writing:

new Dog();

We write:

const dog = new Dog();

Don’t Use the Function Constructor

We shouldn’t use the Function , it takes strings for the function code and returns a function.

It’s a security hazard to run code in strings.

Also, it’s hard to debug and optimize since the code it’s in a string.

For instance, we shouldn’t write:

const add = new Function('a', 'b', 'return a + b');

Instead, we write:

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

Don’t Use the Object Constructor

We shouldn’t use the Object constructor since it doesn’t give us benefit over the create them with object literals.

It just makes the code longer.

For example, instead of writing:

let foo = new Object();

We should write:

let foo = {};

No new with require

We shouldn’t have new and require together.

It may create confusion between:

const foo = new require('foo');

and:

const foo = new (require('foo'));

Therefore, we should avoid these expressions.

Don’t Symbol as a Constructor

Symbol is a factory function. It’s not a constructor.

Therefore, we shouldn’t write:

const foo = new Symbol('foo');

Instead, we write:

const foo = Symbol('foo');

No Primitive Wrapper Instances

We shouldn’t use functions like String or Boolean as constructors.

This is because they return values of type 'object' , which is confusing.

Also, the code is longer.

Therefore, we shouldn’t use it.

For instance, instead of writing:

const message = new String('hi');

We write:

const message = 'hi';

Literals are shorter and eliminate any confusion that can arise.

Don’t Call Global Object Properties as Functions

We shouldn’t call global object properties as functions.

They aren’t mean to be called.

For instance, we shouldn’t write:

const math = Math();

No Octal Literals

We shouldn’t write octal number literals.

They are almost never useful in JavaScript and since they start with a 0, we may confuse it with decimal numbers.

For instance, we shouldn’t write:

const octal = 042;

Instead, we write:

const decimal = 34;

No Octal Escape Sequences

We shouldn’t have octal escape sequences in our code.

They aren’t useful and it’s probably a mistake to have them.

For instance, we shouldn’t write:

const foo = 'foo 251'

Instead, we write:

const foo = 'foo';

No String Concatenation When Using __dirname or __filename

We should use path.join to join paths so that we can use the path in all operating systems correctly.

For instance instead of writing:

const pathToFile = __dirname + '/foo.js'

We write:

const pathToFile = path.join(__dirname, 'foo.js')

Don’t Use proto

The __proto__ property isn’t mean to be accessed directly as indicated by the __ characters.

Instead, we should use the Object.getPrototypeOf method to get the prototype of an object.

So we shouldn’t write:

const foo = obj.__proto__;

But we should write:

const foo = Object.getPrototypeOf(obj);

Don’t Redeclare Variables

We shouldn’t redeclare variables in our code.

We’ll get an error if we declare 2 variables with the same name in the same scope.

Instead, we should reassign a new value to the existing variable with the same name.

For example, instead of writing:

let name = 'james';
let name = 'joe';

We write:

let name = 'james';
name = 'joe';

Conclusion

We shouldn’t declare variables with the same name in the same scope.

Also, we should use getPrototypeOf method to get the prototype of an object.

We should also be mindful when spacing out our JavaScript code.

Categories
JavaScript Best Practices

JavaScript Best Practices — Numbers, Bad Features, and Conditionals

Like any kind of apps, JavaScript apps also have to be written well. Otherwise, we run into all kinds of issues later on.

In this article, we’ll look at some best practices we should follow when manipulating numbers and conditionals.

We also look at bad JavaScript features we shouldn’t use.

Use break to Prevent Fallthrough in switch Cases

We should add break in the end of each case block or statement to prevent cases below the matching case from running.

Therefore, instead of writing:

switch (val) {
  case 1:
    foo()
  case 2:
    bar()
}

We write:

switch (val) {
  case 1:
    foo();
    break;
  case 2:
    bar();
    break;
}

No Floating Decimals

We should start with a 0 in a decimal number if we want to write a decimal between 0 and 1.

It makes everyone clear that it’s a decimal number even though it’s not required by JavaScript.

For example, instead of writing:

const tax = .5;

We write:

const tax = 0.5;

No Reassignment to Function Declarations

We shouldn’t reassign values to function declarations. Overwriting the value of a function declaration is often a mistake or an issue. If we want to be able to change the value, we should use a function expression.

For example, we shouldn’t write:

function foo () { };
foo = otherFunc;

Don’t Assign a Value to Global Variables

Global variables like window and process are for using. We don’t want to reassign a value to it since it’ll overwrite the object and we won’t get any properties in it that we need anymore.

Therefore, we shouldn’t write code like:

window = {};

No Implied eval Calls

Other than eval , there are the Function constructor, setTimeout , and setInterval which also take strings and run them as code.

For instance, we can write:

const fn = new Function('a', 'console.log(a)');

or:

setTimeout('console.log("foo")')

or:

setTimeout('console.log("foo")', 2000)

which are all valid and result in the code in the string being run. It’s a security risk since we can run code from a string. The code in a string is very hard to debug. Also, they can’t be optimized since they’re in a string.

Therefore, we should just write the code.

For instance, we can write:

setTimeout(() => console.log("foo"), 2000)

No Function Declarations in Nested Blocks

Function declarations are supposed to be at the top level only.

JavaScript tolerates it inside a block, but it shouldn’t be there.

Therefore, we shouldn’t have code like:

if (authenticated) {
  function doSomething () {}
}

No Invalid Regex Inside Regex Strings in RexExp Constructors

When we’re using the RegExp constructor to create regexes, make sure that we have valid regex inside the string.

So we shouldn’t write:

const re =new RegExp('[a-z');

but we should write:

const re =new RegExp('[a-z]');

No Irregular Whitespace

We should use normal whitespace characters in our code to avoid issues with parsing it in different runtime environments.

So we shouldn’t have things like:

function foo () /*<NBSP>*/{}

Don’t Use iterator

The __iterator__ property isn’t a standard property in JavaScript objects are creating iterator functions.

Instead, we should use the Symbol.iterator property instead.

So we shouldn’t write something like:

Foo.prototype.__iterator__ = function () {}

Instead, we should write:

const obj = {
  *[Symbol.iterator](){
    //...
  }
}

or:

class Foo {
  *[Symbol.iterator](){
    //...
  }
}

No Labels that Share a Name with an in Scope Variable

We shouldn’t have labels for a loop that shares the same name as a variable inside the loop’s scope.

This causes confusion for both the interpreter and us.

So we shouldn’t write:

let score = 100;
const foo = () => {
  score: while (true) {
    score -= 10;
    if (score > 0) continue score;
    break;
  }
}

No Label Statements

Label statements are labeling loops with a name so we can do some loop operations with it like continue and break .

It’s a rarely used feature in JavaScript.

Therefore, we probably don’t want to use it.

So we shouldn’t have things like:

label:
  while (true) {
    break label
  }

No Unnecessary Nested Blocks

We shouldn’t have nested blocks that are useless.

Nested code is harder to read and useless nesting it’s worse.

So instead of writing:

function foo () {
  {
    bar()
  }
}

We write:

function foo  () {
  bar();
}

Conclusion

We should avoid nonstandard and rarely used constructs.

Also, we shouldn’t have unnecessary code that causes confusion.

Categories
JavaScript Best Practices

JavaScript Best Practices — Promises

Like any kind of apps, JavaScript apps also have to be written well.

Otherwise, we run into all kinds of issues later on.

In this article, we’ll look at some best practices when defining and using promises.

Catching or Returning

We should either catch or return in our promise functions.

For instance,e we can write:

const foo = () => {
  return myPromise.then(doSomething)
}

or we can write:

myPromise
  .then(doSomething)
  .then(doSomethingElse)
  .catch(errors)

Then we catch errors or return a promise that we can us later.

We Should Return Something or Throw Errors

To make us see the result of our promises more clearly, we should either resolve our value or throw something error value.

For instance, if we write:

myPromise.then(function(val) {
  return val * 2
})

Then we resolve our promise to val * 2 .

If we write:

myPromise.then(function(val) {
  throw 'error'
})

Then we reject the promise with the string 'error' .

This is a good practice because we’ll know the value of whether the promise is successful or not.

Consistent Parameter Names when Creating New Promises

We should have consistent parameter names when we’re creating a promise.

We usually write:

new Promise((resolve) => { ... })

or:

new Promise((resolve, reject) => { ... })

This way, it’s clear which parameter is called for resolving the value and which one to call for rejecting the promise.

Return Inside Each then to Create Readable and Reusable Promise Chains

We should return inside each then callback so that we can use it for something else.

For instance, we can write:

myPromise.then((val) => val * 3));

or:

myPromise.then(function(val) { return val * 3; });

This way, we can call it with then or use await with it.

If we have pre-ES6 code, we should have a Promise Constructor Before Creating a Promise

If we still haven’t moved on to ES6, then we need a 3rd party library to create promises.

A popular choice is Bluebird.

We can use it by writing:

const Promise = require('bluebird')
const x = Promise.resolve('foo')

Don’t Nest then or catch

The whole point of using promises is to avoid nesting, so we shouldn’t nest our promise code.

For example, we should write:

myPromise
  .then(doSomething)
  .then(doSomethingElse)
  .catch(errors)

instead of:

myPromise.then(val =>
  doSomething(val).catch(errors)
)

or:

myPromise.then(val =>
  doSomething(val).then(doSomethingElse)
)

No Promises in Callbacks

We shouldn’t nest promises in callbacks as we can see from the previous example.

Instead, we should use promises to reduce nesting.

So we should write:

myPromise
  .then(doSomething)
  .then(doSomethingElse)
  .catch(errors)

Don’t use new on Promise Static Methods

We shouldn’t use new on Promise static methods.

The only situation when we need new is to create a new Promise without using static methods.

For instance, we should write:

Promise.resolve('foo')

or:

Promise.reject('error')

or:

Promise.race([p1, p2])
Promise.all([p1, p2])

No return Statement in finally

We shouldn’t have return statements in finally since nothing can consume what’s returned.

So we should write:

myPromise.finally((val) => {
  console.log('value:', val)
})

Passing the Correct Number of Arguments into Promise Methods

We should ensure that we pass the correct number of arguments into promise methods.

Promise.all takes 1 argument.

Promise.race takes 1 argument.

Promise.resolve is called with 1 argument.

Promise.rejeect is also called with 1 argument.

Promise.then can be called with 1 or 2 arguments.

Promise.catch is called with 1 argument.

Promise.finally is called with 2 arguments.

If we don’t call them with the right arguments, we would get unexpected results.

Use async/await Instead of then

async/await is a shorter syntax to replace then .

For instance, we can write:

const example = async () => {
  const val = await myPromise()
  val = doSomethingSync(val)
  return doSomethingElseAsync(val)
}

instead of:

myPromise()
.then(val => {
  val =  doSomethingSync(val)
  return doSomethingElseAsync(val)
});

It’s shorter and there’s less nesting.

Instead of calling catch , we can use a try/catch block instead:

const example = async () => {
  try {
    let val = await myPromise()
    val = doSomethingSync(val)
    return await doSomethingElseAsync(val)
  } catch (err) {
    errors(err)
  }
}

The catch block is the same as the catch callback.

Conclusion

We should call catch in our promise chain or return something in our callbacks.

Also, we may consider using async/await instead of the then method for chaining promises.

Static methods shouldn’t be called with the new keyword.

Categories
JavaScript Best Practices

JavaScript Best Practices — Imports

Like any kind of apps, JavaScript apps also have to be written well.

Otherwise, we run into all kinds of issues later on.

In this article, we’ll look at some best practices for importing items.

No Imports that can’t be Resolved

We shouldn’t import modules that can’t be resolved.

This means that we make sure that the module is found before we import them.

For ES6 modules, if we write:

import x from './foo'

then we should make sure foo has a default export.

If we write:

const { default: x } = require('./foo')

Then we should make sure that we have a foo module with a module.exports or exports inside.

Named Imports

We should make sure that named imports are included in the set of named exports in a module.

For instance, we should have:

export const foo = "bar";

in foo.js .

Then we can write:

import { foo } from './foo'

or:

export { foo as bar } from './foo'

to import it.

Default Imports

If we want to make a default import, then we should also make sure it exists.

If we have:

export default function () { return 42 }

in foo.js , or:

export const bar = () => { return null }

in bar.js , or:

module.exports = function () { /* ... */ }

in baz.js .

Then we can import them:

import foo from './foo'

or:

import { bar } from './bar'

or:

const baz = require('./baz');

Import a Namespace

We can namespace our exports if they aren’t default exports.

For instance, we can write:

export const a = 1

or:

const b = 2
export { b }

or:

const c = 3
export { c as d }

Given that they’re all in foo.js, we can import them by writing:

import { a } from './foo';

or:

import { b } from './foo';

or:

import { d } from './foo';

We can also combine more than one of them into one.

Absolute paths

We shouldn’t use absolute paths when we’re importing modules.

Therefore, the path must always have a period if we’re importing project files.

For instance, we shouldn’t write:

import f from '/foo';

or:

const f = require('/foo');

Rather, we should write:

import foo from './foo';

or:

const foo = require('./foo');

If we’re importing a module, then we don’t need the slash and the dot.

For instance, we can write:

import _ from 'lodash';

or:

const _ = require('lodash');

Absolute paths are bad since they don’t work all in all locations.

If we move our project to a different part, then the imports will be broken.

No require Calls with Expressions

Using require with expressions is a bad idea. We don’t want to give attackers a chance to change the import path to import malicious code.

require calls are resolved at runtime rather than build time.

Dynamic expressions in require also makes static analysis harder or to find where a module is used in our codebase.

Therefore, instead of writing:

require(name);
require('../' + name);
require(`../${name}`);
require(name());

We write:

require('../foo');

No Internal Modules

We shouldn’t import internal modules that aren’t meant to be imported by external code.

For instance, we shouldn’t write:

import { settings } from './app/index';

Instead, we write:

import { settings } from './app';

No Webpack Loader Syntax

We shouldn’t use Webpack’s proprietary syntax so if we migrate off Webpackm then we won’t have so many issues.

Instead of writing:

import theme from 'style!css!./theme.css';

We write:

import theme from './theme.css';

Using standard syntax will save us a lot of trouble in the future.

Don’t Import Itself in a Module

We shouldn’t import itself in a module.

So if we have foo.js , we shouldn’t write:

import foo from './foo';

or:

const foo = require('./foo');

Instead, we should write the following in foo.js :

import bar from './bar';

We can also write:

const bar = require('./bar');

No Cyclic Imports

We shouldn’t have cyclic imports in our modules.

A cyclic import would be the following:

a.js :

import './b.js';

b.js :

import './a.js';

We have 2 modules that import dependencies from each other.

Instead, we should write:

a.js :

import './c.js';

to break the cycle.

Conclusion

We shouldn’t have cyclic imports in our code.

Also, we shouldn’t have dynamic imports or imports that can’t be imported.

And we should never have absolute imports.