Categories
JavaScript Best Practices

JavaScript Best Practices — Spread, Rest, and Promises

To make code easy to read and maintain, we should follow some best practices.

In this article, we’ll look at some best practices we should follow to make everyone’s lives easier.

Replace the Use of the RegExp Constructor in favor of Regular Expression Literals

Regex literals are much shorter than using the RegExp constructor.

So we should use them in favor of the constructor.

For instance, instead of writing:

new RegExp("^\d.$");

We write:

/^\d.$"/

Using the Rest Parameters Instead of arguments

The argyments object doesn’t work with arrow functions and it’s not an array.

Rest parameters return an array of the arguments.

So we should use that instead.

For instance, instead of writing:

function foo() {
  console.log(arguments);
}

We write:

function foo(...args) {
  console.log(args);
}

Use Spread Syntax Instead of .apply()

We should use the spread syntax instead of apply .

For instance, instead of writing:

const args = [1, 2, 3, 100, 200\];
Math.max.apply(Math, args);

We write:

const args = [1, 2, 3, 100, 200];
Math.max(...args);

We don’t need apply unless we want to change the value of this .

Use Template Literals Instead of String Concatenation

We should use template literals instead of string concatenation.

It’s much easier to include expressions in template literals.

For instance, instead of writing:

const str = "hello, " + name + "!";

We write:

const str = `hello, ${name}!`;

Return Inside Each then() to Create Readable and Reusable Promise Chains

We should return something if we want to call then on the promise.

For instance, instead of writing:

myPromise.then(() => {
  doSomething()
})

We write:

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

Use catch() on Un-returned Promises

We should use catch on unreturned promises to catch any errors to occur with them.

For instance, we can write:

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

where errors is a callback to catch any error that occurred from the promises.

Make Sure to Create a Promise Constructor Before Using it in an ES5 Environment

The Promise constructor is introduced with ES6.

So any code written with older versions of ES must use a promise library to create promises.

For instance, instead of writing:

const x = Promise.resolve('foo');

We write:

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

No Nested then() or catch() Statements

We should avoid nested then or catch statements.

For instance, instead of writing:

myPromise.then(val =>
  doWork(val).then(doMore)
)

We write:

myPromise
  .then(doWork)
  .then(doMore)
  .catch(errors)

Avoid Using new on a Promise Static Method

If we are calling Promise static methods, then we shouldn’; use the new keyword.

For instance, instead of writing:

new Promise.resolve(value)
new Promise.reject(error)
new Promise.race([foo, bar])
new Promise.all([foo, bar])

We write:

Promise.resolve(value)
Promise.reject(error)
Promise.race([foo, bar])
Promise.all([foo, bar])

No return Statements in finally()

return statements in finally is useless since nothing will consume the result.

Therefore, instead of writing:

myPromise.finally((val) => {
  return val
})

We write:

myPromise.finally((val) => {
  console.log(val)
})

No Wrapping Values in Promise.resolve or Promise.reject when not Needed

We shouldn’t wrap values in Promise.resolve or Promis.reject when they aren’t needed.

For instance, instead of writing:

myPromise.then((val) => {
  return Promise.resolve(val * 3);
})
myPromise.then((val) => {
  return Promise.reject('foo');
})

We write:

myPromise.then((val) => {
  return val * 3
})
myPromise.then((val) => {
  throw 'foo'
})

return will return a promise with the resolved value being the return value.

throw will reject a promise with the given value.

Consistent Parameter Names when Creating New Promises

We should have consistent parameter names when creating new promises.

This way, there won’t be any confusion as to what they do.

For instance, instead of writing:

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

which have the functions in the wrong order or:

new Promise((ok, fail) => { ... })

which have nonstandard names, we write:

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

Prefer async/await to the Callback Pattern

async and await is shorter than the callback pattern, so we can sue that instead.

For instance, instead of writing:

myPromise
  .then((val) => {
    return foo();
  })
  .then((val) => {
    return bar();
  })
  .then((val) => {
    return val;
  })

We write:

const foo = async () => {
  const val1 = await myPromise;
  const val2 = await foo();
  const val3 = await bar();
  return va3;
}

Conclusion

We should use spread and rest instead of apply and arguments .

Promises can be cleaned up in many ways.

Categories
JavaScript Best Practices

JavaScript Best Practices — Spaces, Functions, Negations

To make code easy to read and maintain, we should follow some best practices.

In this article, we’ll look at some best practices we should follow to make everyone’s lives easier.

No Functions in Loops

Functions in loops result in errors because of how they’re created in a loop.

With var , the loop index is always the last since it doesn’t get passed into the variable function until it’s done.

Instead of writing:

for (var i = 0; i < 10; i++) {
  funcs[i] = function() {
    return i;
  };
}

We write:

for (let i = 0; i < 10; i++) {
  funcs[i] = function() {
    return i;
  };
}

to get the expected value of i in the function returned, which is the index value of the loop during each iteration.

With var , the value will always be 10.

No Magic Numbers

Magic numbers are hard to understand and we’ve to change them in multiple places, so we should assign them to named constants.

Instead of writing:

const now = Date.now();
const anHourLater = now + (60 * 60 * 1000);

We write:

const SECONDS_IN_HOUR = 60 * 60;

No Characters which are Made with Multiple Code Points in Character Class Syntax

We shouldn’t have characters that are made of multiple code points.

This is because they’ll be matched to either character.

So w should write:

/^[a]$/u.test("a")

instead of:

/^[Á]$/u.test("Á")

No Mixes of Different Operators

Mixing different operators are confusing, so we shouldn’t do them.

For instance, instead of writing:

const foo = a && b || c || d;

We put in some parentheses to separate the expressions:

const foo = (a && b) || c || d;

require Calls to be Mixed with Regular Variable Declarations

Instead of writing require calls separated with regular variables with commas, we just write them all in their own line for clarity.

For instance, instead of writing:

const eventEmitter = require('events').EventEmitter,
  foo = 10,
  bar = 'baz';

We write:

const eventEmitter = require('events').EventEmitter;
const foo = 10;
let bar = 'baz';

No Mixing Spaces and Tabs for Indentation

Mixing spaces and tabs for indentation will cause issues with parsing and formatting with text editors, so we should convert tabs into spaces.

No Use of Chained Assignment Expressions

Chained assignment expressions will create global variables.

The ones that aren’t next to the variable declaration keyword will be global.

They’re also hard to read.

For instance, if we have:

const foo = bar = 0;

then bar is global and foo is constant.

Instead, we separate them for clarity:

const foo = -0;
const bar = 0;

No Multiple Spaces

One space is enough for anything other than indentation.

So we should keep a single space for expressions.

Instead of writing:

if(foo  === "baz") {}

We write:

if(foo === "baz") {}

No Multiline Strings

We shouldn’t write multiline strings with a backslash.

It’s not standard and it’s bad.

So instead of writing:

let x = "line 1
  line 2";

We write:

let x = `line 1
  line 2`;

We use template strings instead of regular strings for multiline strings.

No Multiple Empty Lines

Multiple empty lines aren’t useful for separating code.

We just need one.

So instead of writing:

let foo = 5;

var bar = 3;

We write:

let foo = 5;

var bar = 3;

No Reassignment of Native Objects

We should never reassign native objects to anything else.

We don’t want any unexpected results because of that.

So instead of writing:

Object = null
undefined = 1

We write:

let foo = null;
let bar = 1;

No Negated Conditions

Negated conditions are hard to read, so we should avoid them as much as possible.

For instance, instead of writing:

if (!a) {
  doSomething();
} else {
  doMore();
}

We write:

if (a) {
  doSomething();
} else {
  doMore();
}

Conclusion

We don’t need multiple spaces in our code.

Negated conditions should be replaced with positive ones.

Magic numbers should be replaced with named constants.

Be careful that we don’t have regex with characters that have multiple code points.

Spaces and tabs shouldn’t be mixed.

Categories
JavaScript Best Practices

JavaScript Best Practices — Duplicates, Returns, Eval, and Built-in Prototypes

To make code easy to read and maintain, we should follow some best practices.

In this article, we’ll look at some best practices we should follow to make everyone’s lives easier.

No Duplicate Conditions in if-else-if Chains

We shouldn’t have duplicate conditions in if-else-if chains.

Only the first one will run.

So instead of writing:

if (isSomething(x)) {
  foo();
} else if (isSomething(x)) {
  bar();
}

We write:

if (isSomething(x)) {
  foo();
} else if (isSomethingElse(x)) {
  bar();
}

No Duplicate Keys in Object Literals

We shouldn’t have duplicate keys in object literals.

For instance, we shouldn’t have objects like:

const foo = {
  bar: "baz",
  bar: "bar"
};

Instead, we write:

const foo = {
  bar: "baz"
};

No Duplicate Case Label

We shouldn’t have duplicate case labels in our code.

For instance, instead of writing:

switch (a) {
  case 1:
    break;
  case 2:
    break;
  case 1:
    break;
  default:
    break;
}

We write:

switch (a) {
  case 1:
    break;
  case 2:
    break;
  default:
    break;
}

No Duplicate Imports

We shouldn’t have multiple, import statements for one module.

For instance, instead of writing:

import { merge } from 'module';
import something from 'foo';
import { find } from 'module';

We write:

import { merge, find } from 'module';
import something from 'foo';

No return Before else

If we’re writing a return statement, then we can remove the else

For instance, we can write:

function foo() {
  if (x) {
    return y;
  }
  return z;
}

instead of writing:

function foo() {
  if (x) {
    return y;
  } else {
    return z;
  }
}

No Empty Block Statements

Empty blocks aren’t very useful, so we should fill them with something or remove them.

For instance, the following aren’t very useful:

if (bar) {}

while (bar) {}

switch (bar) {}

try {
  doSomething();
} catch (ex) {

} finally {

}

for (;;){}

Instead, we fill them with something.

Empty Character Classes in Regex

We shouldn’t have empty character classes in regex since they don’t match anything.

For instance, we should have code like:

/^foo[]/.test("foobar");
"foobar".match(/^foo[]/);

Instead, we should fill the brackets with a pattern:

/^foo[a-z]/.test("foobar");
"foobar".match(/^foo[a-z]/);

No Empty Functions

We shouldn’t have empty functions in our code.

They don’t do anything.

For instance, instead of writing:

function foo() {

}

We write:

function foo() {
  doSomething();
}

No Empty Destructuring Patterns

Empty destructuring patterns do nothing, and they’re easily mistaken for empty objects as the default value.

For instance:

const {a: {}} = foo;

is easily mistaken for:

const {a = {}} = foo;

The first is an empty destructuring pattern.

The 2nd is using the empty object as the default value.

No Null Comparisons

Comparing null using == or != can have unintended consequences because of the data type coercion.

Therefore, we should use === or !== to do the comparison.

Instead of writing:

if (foo == null) {
  bar();
}

while (qux != null) {
  bar();
}

We write:

if (foo === null) {
  bar();
}

while (qux !== null) {
  bar();
}

No Calls to eval()

eval lets us run JavaScript code from a string.

But we shouldn’t use it since it’s insecure to run code from a string.

Performance optimizations also can’t be done on code that’s in a string.

Instead of writing:

eval("let a = 0");

We write:

let a = 0;

No Reassigning Exceptions in catch Clauses

We shouldn’t reassign exceptions ib catch clauses.

If we do, then we lose data from the exception.

So instead of writing;

try {
  // code
} catch (e) {
  e = 10;
}

We write:

try {
  // code
} catch (e) {
  const foo = 1;
}

Don’t Extend Native Objects

We shouldn’t extend built-in objects, even though we’re allowed to.

For instance, we shouldn’t write code like:

Object.prototype.foo = 55;

to add properties to a built-in constructor.

Using defineProperty is also bad:

Object.defineProperty(Array.prototype, "bar", { value: 999 });

Conclusion

We shouldn’t have duplicate conditions, keys, or case labels.

Also, we should never use eval .

And we shouldn’t extend native objects.

Empty destructuring patterns are also useless and deceptive.

We should compare null with === or !== .

Categories
JavaScript Best Practices

JavaScript Best Practices — Comments, Nesting, and Parameters

To make code easy to read and maintain, we should follow some best practices.

In this article, we’ll look at some best practices we should follow to make everyone’s lives easier.

No Commented Out Code in our Codebase

We shouldn’t have commented out code in our codebase.

We have version control to keep a record of our changes.

So we should remove them.

If we have:

doStuff();
// doMoreStuff();

Then we should rewrite it to:

doStuff();

No Journal Comments

We shouldn’t have journal comments since version control has the code change records.

For instance, we shouldn’t write:

/**
 * 2019-02-03: Removed type-checking
 * 2018-03-14: Added add function
 */
function add(a, b) {
  return a + b;
}

We write:

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

No Positional Markers

Positional markers are just noise, we shouldn’t have them in our code.

Instead of writing:

////////////////////////////////////////////////////////////////////////////////
// initialize setup data
////////////////////////////////////////////////////////////////////////////////
const init = function() {
  // ...
};

We write:

const init = function() {
  // ...
};

Maximum Number of Classes Per File

We may want to enforce a maximum number of classes per file to reduce a file’s complexity and responsibility.

For instance, we may limit the number of classes to 3 max:

class Foo {}
class Bar {}
class Baz {}

Maximum Depth that Blocks can be Nested

Nested code is hard to read.

Therefore, we should eliminate them as much as possible.

One or 2 levels of nesting are probably the max that can be tolerated.

For instance, don’t write

function foo() {
  for (;;) {
    while (true) {
      if (true) {
        if (true) {
        }
      }
    }
  }
}

Instead, we write:

function foo() {
  for (;;) {

  }
}

We can use guard clauses to return early.

For instance, we can write:

function foo() {
  if (!cond) {
    return;
  }
  while (true) {

  }
}

instead of:

function foo() {
  if (!cond) {
    while (true) {

    }
  }
}

We can also use break or continue to stop a loop or move to the next iteration of it.

Maximum Line Length

We’ve to scroll horizontally to read long lines.

Therefore, we shouldn’t write them.

For instance, we shouldn’t write:

const foo = { "foo": "This is a foo.", "bar": { "qux": "This is a bar"}, "baz": "This is a baz" };

It overflows the line.

Instead, we write:

const foo = {
  "foo": "This is a foo.",
  "bar": {
    "qux": "This is a bar"
  },
  "baz": "This is a baz"
};

Each line is much shorter and easier to read.

We can do the same with comments.

Maximum File Length

Big files are complex and hard to understand.

Therefore, we should divide them into smaller files.

300 to 500 lines max is usually the recommendation.

Maximum Function Length

We should make sure our function only does one thing.

Therefore, we should reduce the length of our functions.

For instance, we can write:

function foo() {
  let x = 0;
}

which is a short, simple function.

Maximum Depth that Callbacks can be Nested

We often have to run callbacks for both synchronous and async code.

We’ve to run many of them to do all the things we want.

The worst way to write them is to nest them deeply.

So we shouldn’t write things like:

foo(function() {
  bar(function() {
    qux(function() {
      abc(function() {

      });
    });
  });
});

That’s just very hard to read and debug, especially if we have lots of code in each callback.

We should use alternatives like promises.

Maximum Number of Parameters in Function Definitions

We shouldn’t have too many parameters in a function.

The more we have, the harder it is to read.

For instance, instead of writing:

function foo(bar, baz, qux, abc) {
  doSomething();
}

We write:

function foo({ bar, baz, qux, abc }) {
  doSomething();
}

If we need lots of parameters, we can take an object and destructure them.

Conclusion

We shouldn’t have commented out code or other useless comments.

Nesting, function length, and the number of parameters should be minimized.

The number of classes should also be reduced.

A file shouldn’t have too many lines of code.

Categories
JavaScript Best Practices

JavaScript Best Practices — Things that Don’t Belong in Production Code

To make code easy to read and maintain, we should follow some best practices.

In this article, we’ll look at some best practices we should follow to make everyone’s lives easier.

No Assignment Operators in Conditional Statements

Assignment operators in conditional statements are probably a mistake.

If that’s all we have, then we should check if that’s actually what we want.

For instance, instead of writing:

if (user.title = "manager") {
  // ...
}

We write:

if (user.title === "manager") {
  // ...
}

No Arrow Functions that can be confused with Comparisons

It’s easy to confuse the arrow of the arrow function and comparison operators.

For instance, if we have:

const foo = a => 1 ? 2 : 3;

Then we can be confused easily.

To make it clear, we write:

const foo = (a) => (1 ? 2 : 3);

Remove console Before Going to Production

We should remove console before our code goes to production.

It’s good for debugging but not suitable for end-users to view the output.

If we have something like:

console.log("foo bar");

We should remove them.

Don’t Modify Variables that are Declared Using const

Variables shouldn’t be reassigned if they’re declared with const .

We’ll get an error if we try to do so.

So instead of writing:

const a = 0;
a = 2;

or:

const a = 0;
a += 1;

We write:

const a = 0;
const b = a + 1;

No Constant Expressions in Conditions

We shouldn’t have constant expressions in conditions.

They either always run or never run.

This makes them useless.

For instance, instead of writing:

if (false) {
  doSomething();
}

which never runs, we write:

if (foo && bar) {
  doSomething();
}

Returning Value in Constructor

If we return a value in a constructor, then the object is returned.

Otherwise, we return the instance of the constructor.

The latter case is probably what most people want.

So we shouldn’t return our own object most of the time.

Instead of writing:

class A {
  constructor(a) {
    this.a = a;
    return a;
  }
}

We write:

class A {
  constructor(a) {
    this.a = a;
  }
}

to return an instance of A instead of the value of a .

continue Statements

contunue statements let us skip to the next iteration of the loop.

For instance, we can write:

for (i = 0; i < 10; i++) {
  if (i >= 5) {
    continue;
  }
  //...
}

to skip to the next iteration if i is bigger than or equal to 5.

This can be useful.

Control Characters in Regular Expressions

Control characters are rarely used in regex or JavaScript in general, so it’s probably a mistake to have them.

Instead of writing:

const pattern1 = /x1f/;
const pattern2 = new RegExp("x1f");

We write:

const pattern1 = /d/;
const pattern2 = new RegExp("d");

Don’t Use debugger in Production Code

Before our code goes to production, we should remove all debugger statements so that the app runs properly.

If we don’t, then it’ll pause when we hit the statement.

Instead of writing:

function toBool(x) {
  debugger;
  return Boolean(x);
}

We write:

function toBool(x) {
  return Boolean(x);
}

No Deleting Variables

The delete operator is only used for deleting variable properties.

It can’t be use with variables.

So we shouldn’t have code like:

let x;
delete x;

No Regular Expressions That Look Like Division

We shouldn’t have a regex that looks like division.

For instance, code like:

/=foo/

is probably a mistake.

Instead, we write:

/foo/

No Duplicate Arguments in Function Definitions

Duplicate parameters is an illegal syntax in JavaScript, so we shouldn’t write that.

For instance, instead of writing:

function foo(a, b, a) {
  console.log(a);
}

We write:

function foo(a, b) {
  console.log(a);
}

No Duplicate Name in Class Members

We shoukldn’r have duplicate names in class members

The one that comes last will overwrite the earlier instances.

So instead of writing:

class Foo {
  bar() {
    console.log("bar");
  }
  bar() {
    console.log("baz");
  }
}

We write:

class Foo {
  bar() {
    console.log("baz");
  }
}

Conclusion

We shouldn’t have duplicate parameters and class members.

We probably shouldn’t have assignment operators in if statements.

console and debugger don’t belong in production code.