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.

Categories
JavaScript Best Practices

JavaScript Best Practices — Newer Syntax, Formatting, and Promise Rejections

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.

Placing Object Properties on Separate Lines

We should place object properties on separate lines if we have many of them.

For instance, instead of writing:

const obj = {a: 1, b: [2, {c: 3, b: 4}]};

We write:

const obj = {
  a: 1,
  b: [2, {
    c: 3,
    b: 4
  }]
};

Now it won’t overflow the page.

Object Literal Shorthand Syntax

We should use the literal shorthand syntax to save typing and space.

For instance, instead of writing:”

const bar = {
  x: x,
  y: y,
  z: z,
};

We write:

const bar = {
  x,
  y,
  z,
};

For methods, instead of writing:

const bar = {
  x: function(){},
  y: function(){}
};

We write:

const bar = {
  x(){},
  y(){}
};

Newlines Around Variable Declarations

We want to add new lines to variable declarations if they’re long.

If we have:

var a, b;

Then we can leave it.

If it’s longer, then we break them into their own lines.

Linebreak Style for Operators

We can place the line breaks where we want as long as we’re consistent.

For instance, we can write:

const fullHeight = paddingTop +
  innerHeight +
  paddingBottom;

or:

const fullHeight = paddingTop
  + innerHeight
  + borderBottom;

No Padding within Blocks

We can skip paddinbg within blocks.

For instance, instead of writing:

if (a) {

  b();

}

We write:

if (a) {
  b();
}

Padding Lines Between Statements

We can add padding lines between statements to group them.

For instance, we can write:

function foo() {
  let a = 1;

  return a;
}

Using Arrow Functions for Callbacks

If we don’t need to reference their ownthis in our callback, then we should use arrow functions for callbacks.

For instance, instead of writing:

foo(function(a) { return a; });

We write:

foo(a => a);

Use const

We can declare constants with const .

We can’t assign them to a new value once we defined it.

For instance, we can write:

const a = 0;

or:

for (const a of [1, 2, 3]) {
  console.log(a);
}

Prefer Destructuring from Arrays and Objects

We can use the destructuring assignment syntax for arrays of objects.

They let us break their entries into variables.

For instance, instead of writing:

const foo = array[0];

We write:

const [foo] = array;

And instead of writing:

const foo = object.foo;

We write:

const { foo } = object;

Replace Math.pow in favor of the ** Operator

We can do exponentiation with the ** operator instead of using Math.pow .

It’s shorter and we don’t have to call a method.

Instead of writing:

const foo = Math.pow(2, 8);

We write:

const foo = 2 ** 8;

Use Named Capture Group in Regular Expression

Since ES2018, we can name our capture groups.

This makes the pattern clearer for everyone.

For instance, instead of writing:

const regex = /(?d{4})/;

We write:

const regex = /(?<year>d{4})/;

Now we know we’re looking for the year number.

Replace parseInt() and Number.parseInt() with Binary, Octal, and Hexadecimal Literals

We can replace parseInt and Number.parseInt calls with the number literals instead.

For instance, instead of writing:

parseInt("1010101", 2)

and:

Number.parseInt("1010101", 2)

We can write:

0b1010101

It’s much shorter.

Use Object Spread Over Object.assign

We can use the spread operator with objects,

Therefore, we should use them since it’s shorter.

For instance, instead of writing:

Object.assign({}, {foo: 'bar'})

We write:

const obj = {foo: 'bar'};
const copy = {...obj};

Use Error Objects as Promise Rejection Reasons

We should use Error instances for promise rejection reasons.

They include the stack trace, which tells us useful information for debugging.

We need it to find where the error comes from.

For instance, instead of writing:

Promise.reject('error');

We write:

Promise.reject(new Error("error"));

Conclusion

The Error instance tells us more information than other objects for errors.

We can also format our code with linebreaks and spaces.

With new syntax, we can write less code.

They include the object spread, exponentiation, and destructuring assignments syntax.

Also, we can use named capture groups for regex.

Categories
JavaScript Best Practices

JavaScript Best Practices — Comments, Imports, and Indentation

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 Cyclic Imports

We shouldn’t have cyclic imports. They won’t work with ES modules.

For instance, we shouldn’t have:

dep-c.js

import './dep-a.js'

dep-b.js

import './dep-c.js'

dep-a.js

import { b } from './dep-b.js'

We import a in c, c in b and b in a .

This means that we have a cyclic dependency between the 3 modules.

No Default Export

We shouldn’t have default exports without a name.

For instance, we shouldn’t write:

export default 'bar';

It’s good to have a name to identify it.

Instead, we write:

export default name = 'bar';

No Duplicate Import

We shouldn’t have duplicate imports.

For instance, we shouldn’t write:

import Foo from './mod'
import { Bar } from './mod'

We have 2 lines importing the same module.

Instead, we write:

import Foo, { Bar } from './mod'

No Dynamic Require

We shouldn’t have dynamic require calls. This is because we shouldn’t let people pass anything as the value to improve security.

For instance, instead of writing:

require(name);

or:

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

We write:

require('../name');

No Extraneous Packages

If we aren’t using a package, then we shouldn’t import it.

So if we have:

import _ from 'lodash';

Then we should use it.

No Mutable Exports

Mutable exports will change the value of the export when it’s exported to the latest value. So we shouldn’t let them be mutable.

For instance, if we have:

export let count = 2;

or:

export var count = 3;

Then we can change the value of count .

No Node Modules on Client Side

If we’re building a client-side JavaScript project, then we shouldn’t import Node modules since they can’t be used.

For instance, we shouldn’t have:

import fs from 'fs';
import path from 'path';

or:

const fs = require('fs');
const path = require('path');

in a client-side project.

Don’t Let a Module Importing Itself

A module shouldn’t import itself. It works but it’s pointless.

So we shouldn’t write

import foo from './foo';

or:

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

Instead, we write:

import bar from './bar';

or:

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

No Unassigned Imports

If we have an import that can be used unassigned, then that means the module has side effects or it’s not being used. They’re both bad, so we should consider exporting members and not committing side effects in modules.

So instead of writing:

import 'foo'

or:

require('foo')

We write:

import _ from 'foo'

or:

import {foo as bar} from 'foo'

No Useless Path Segments

We shouldn’t have useless path segments in our imports.

For instance, We shouldn’t have:

import "./../pages/about.js"

since . and .. cancel each other out.

So we write:

import "./pages/about.js"

We should also remove extra slashes.

So instead of:

import "./pages/";

We write:

import "./pages";

Use Default Export for Single Modules

We should use default export for single modules if there’s only one. Then we can name that export whatever we want.

For instance, instead of writing:

export const foo = 'foo';

We write:

const foo = 'foo';
export default foo;

Consistent Indentation

2 spaces are good for indentation. It minimizes typing and space but still keeps everything clear. So we can use that for indentation.

Initialization in Variable Declarations

We can make sure that we declare variables and assign a value to them so we can use them.

So we can write:

var foo = 1;

or:

let foo = 1;

instead of:

var foo;

or:

let foo;

const always have to have a value assigned to them.

Multiline Comments Style

Multiline comments are easy to write and read. We can use them to write longer explanations.

For instance, we can write:

/*
 * this line
 * calls foo() _*/_

We write them with an asterisk on the left separated by a space from the rest of the text.

Conclusion

We should write imports properly. Also, we should consider default exports for exporting one member of a module only. Cyclic and self imports don’t work well, so we should avoid them. Multiline comments are useful for writing longer explanations.

Categories
JavaScript Best Practices

JavaScript Best Practices — Useless Operations and Deprecated Statements

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 Unused Labels

We shouldn’t have unused labels in our code.

For instance, instead of writing:

B: {
  foo();
}

We write:

foo();

No Unnecessary Computed Property Keys in Objects and Classes

If we’re going to put in strings into the brackets, then we should use other alternatives.

For instance, instead of writing:

const foo = {["a"]: "b"};

We write:

const foo = {"a": "b"};

or

const foo = { a: "b" };

No Unnecessary Concatenation of Strings

We shouldn’t concatenate strings if we don’t need to do them.

For instance, instead of writing:

const foo = "foo" + "bar";

We write:

const foo = "foobar";

No Unnecessary Constructor

We shouldn’t write unnecessary constructors in our code. JavaScript will add them in some cases automatically.

For instance, we can write:

class A {
  constructor() {}
}

or

class B extends A {
  constructor(value) {
    super(value);
  }
}

Instead, we write:

class A {
  constructor() {
    doWork();
  }
}

or:

class B extends A {
  constructor() {
    super();
    super('foo');
  }
}

Unnecessary Escape Usage

We shouldn’t escape non-special characters in strings, template literals, and regexes.

For instance, we shouldn’t write:

let foo = "hola";

Instead, we write:

let foo = "hola";

No Renaming import, export, and Destructured Assignments to the Same Name

We shouldn’t rename imports, exports, and restructured assignments to the same name.

For instance, we shouldn’t write:

import {
  baz as baz
} from "bar";

export {
  baz as baz
};

const {
  foo: foo
} = bar;

Instead, we write:

import { baz } from "bar";
export { baz};
let { foo } = bar;

No Redundant return Statements

We shouldn’t have redundant return statements in our code.

For instance, the following have useless return statements:

function foo() {
  return;
}

function foo() {
  doWork();
  return;
}

Instead, we write:

function foo() {

}

function foo() {
  doWork();
}

Use let or const instead of var

let or const are block-scoped, so they remove lots of confusion with var .

For instance, instead of writing:

var x = "y";
var CONFIG = {};

We write:

let x = "y";
const CONFIG = {};

Don’t Use the void Operator

The void operatior always return undefined , so there’s no point in using it.

For instance, instead of writing:

function baz() {
  return void 0;
}

We write:

function baz() {
  return undefined;
}

No Warning Comments

Instead of adding comments for warnings, we should fix the traps indicated in the comments.

Instead of writing:

// TODO: fix this
// FIXME: bad code

We do the things in the comment.

No Whitespace Before Properties

We shouldn’t have whitespace before properties.

So we don’t write:

foo [bar]

foo. bar

Instead, we write:

foo[bar]

foo.bar

No with Statements

with statements are confusing because we may confuse the variable inside with what’s outside. So we shouldn’t use it, and it’s not allowed with strict mode.

For instance, instead of writing:

with(dimensions) {
  area = width * height;
}

We write:

const area = ({width, height}) => width * height;

Location of Single-Line Statements

If we have single-line statements, then they should be always in the same location so that we won’t be confused by their location.

For instance, instead of writing:

if (foo)
  bar();
  baz();

We write:

if (foo)
  bar();
baz();

So we won’t be confused with their location.

Enforce Consistent Line Breaks Inside Braces

We should have consistent line breaks inside braces. Diffing is easier if they’re consistent.

For instance, we should write:

let b = {
  foo: 1
};

and stick with it.

Consistent Spacing Inside Braces

Like with line breaks, we should have consistent spacing inside braces.

For instance, we can write:

const obj = { foo: "bar" };

and stick with it.

Conclusion

We should keep our spacing consistent for easy digging. Also, we shouldn’t do unnecessary operations or have useless expressions. Useless constructors should also be removed. with statements should never be used.

Categories
JavaScript Best Practices

JavaScript Best Practices — Statements, Chaining, and Constructors

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.

Maximum Number of Statements Per Line

We shouldn’t have too many statements per line.

For instance, we can write things like:

function foo () { let  bar; let baz; if (condition) { bar = 1; } else { baz = 2; } return true; }

Instead, we write:

function foo() {
  let bar;
  let baz;
  if (condition) {
    bar = 1;
  } else {
    baz = 2;
  }
  return true;
}

Require Constructor Names to Begin with a Capital Letter

Constructor names start with a capital letter to distinguish from other identifiers.

For instance, we write;

const person = new Person();

It’s a convention that is commonly accepted so people will understand it.

Parentheses When Invoking a Constructor with no Arguments

We should always add parentheses to the constructor when we invoke them with no arguments.

We can skip the parentheses, but we should keep them.

Instead of writing:

const person = new Person;

We write:

const person = new Person();

Empty Line After Variable Declarations

We can group variable declarations together.

For instance, we can write:

const greet = "hello,",
const name = "james";

const foo = 12;

We can group them with empty lines to distinguish them easily.

Empty Line Before return Statements

An empty line before statements aren’t that useful, so we can skip them.

We can write:

function foo(baz) {
  if (!baz) {
    return;
  }
  return baz;
}

and save some space.

Newline After Each Call in a Method Chain

If we have long method chains, we should put a newline after each call.

For instance, we can write:

$("#p")
  .css("color", "green")
  .slideUp(2000)
  .slideDown(2000);

instead of writing:

$("#p").css("color", "green").slideUp(2000).slideDown(2000);

Don’t Use Alert for Debugging

We shouldn’t use alert for debugging.

There are better alternatives like console and debugger for debugging.

However, they can used for showing alerts like it’s intended to.

Array Constructors

The Array constructor is good for creating empty arrays.

However, other things that it can do should be avoided in favor for array literals.

We can create an empty array by writing:

Array(10)

Then we get an array with 10 empty slots.

Then we can fill them with fill :

Array(10).fill().map((_, i) => i);

We call fill and map to fill the empty array with content.

Bitwise Operators

Since most people aren’t aware of JavaScript bitwise operators, we may want to check for them so that they won’t be used by mistake.

|| and && look like the | and & bitwise operators.

Use of caller/callee

We shouldn’t use arguments.caller and arguments.callee to get the caller and callee of a function.

It can’t be used in strict mode.

Lexical Declarations in case/default Clauses

We should use case and defaukt blocks so we can declare block scope variables with the same name in different blocks.

For instance, instead of writing:

switch (foo) {
  case 1:
    let x = 1;
    break;
  case 2:
    const y = 2;
    break;
  case 3:
    function f() {}
    break;
  default:
    class C {}
}

We write:

switch (foo) {
  case 1: {
    let x = 1;
    break;
  }
  case 2: {
    const x = 2;
    break;
  }
  case 3: {
    function f() {}
    break;
  }
  default: {
    class C {}
  }
}

We can declare x in different blocks since they’re block-scoped.

Modifying Variables of Class Declarations

We shouldn’t modify the value of class declarations.

So we shouldn’t write:

class A { }
A = 0;

We won’t get an error, but it’ll confusing to people.

No Comparing Against -0

=== doesn’t distinguish between 0 and -0.

So comparing them wouldn’t return what we expect.

For instance,e instead of writing:

if (x === -0) {
  doSomething()
}

We should use Object.is for comparison, which does distinguish between +0 and -0:

if (Object.is(x, -0)) {
  doSomething();
}

Conclusion

We shouldn’t have too many statements in one line.

Anything in the arguments object shouldn’t be used.

Bitwise operators are easily mistaken for boolean operators.

Use Object.is to compare against -0.

Constructors should always be invoked with parentheses.