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.

Categories
JavaScript Best Practices

JavaScript Best Practices — Unary Operators, Configs, and Useless Expressions

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.

Unary Operators ++ and --

++ and -- both do assignment and return the value.

Their placement can change the meaning of the code.

So, we should consider using alternatives.

For instance, instead of writing:

let i = 10;
i++;

We write:

let i = 10;
i += 1;

Use of process.env

We may use configurations instead of setting environment variables.

They’re easy to replace.

For instance, instead of writing:

if (process.env.NODE_ENV === "development") {
  //...
}

We write:

const config = require("./config");

if (config.env === "development") {
  //...
}

No process.exit()

process.exit() ends the program immediately.

This means that we don’t have a chance to handle errors gracefully.

So instead of writing:

if (error) {
  process.exit(1);
}

Instead, we write:

if (error) {
  throw new Error("error happened");
}

No Use of __proto__

__proto__ is a hidden property of an object which has its prototype.

We should use Object.setPrototypeOf and Object.getPrototypeOf to set and get the prototype of an object respectively.

For instance, instead of writing:

const a = obj.__proto__;

obj.__proto__ = b;

We write:

const a = Object.getPrototypeOf(obj);

Object.setPrototypeOf(obj, b);

No Use of Object.prototypes Built-in Properties Directly

We shouldn’t use Objetyc.prptotype built-in properties directly since they may be overridden by anyone.

For instance, foo.hasOwnProperty can be overridden by our own method.

Instead of writing;

const hasBazProperty = foo.hasOwnProperty("baz");

We write:

const hasBazProperty = Object.prototype.hasOwnProperty.call(foo, "baz");

No Variable Redeclaration

We shouldn’t redeclare variables, even though we can with var .

For instance, we shouldn’t write:

var a = 3;
var a = 123;

Instead, we write:

var a = 3;
a = 123;

Multiple Spaces in Regular Expression Literals

We shouldn’t make our regex literals more complex with extra spaces.

They’re hard to read, and we can replace them with curly braces.

For instance, instead of writing:

const re = /foo     bar/;

We write:

const re = /foo {5}bar/;

No Assignment in return Statement

We shouldn’t assign values ina returnm statement.

It’s probably a mistake if we do.

We probably want to do a comparison.

For instance, instead of writing:

function doWork() {
  return foo = bar + 2;
}

We write:

function doWork() {
  return foo === bar + 2;
}

No Unnecessary return await

We don’t need to use return await together.

Using await with return just add an extra task to the microtask queue.

async functions always return a promise, so we can just return the promise directly instead of awaiting it before doing so.

For instance, instead of writing:

async function foo() {
  return await bar();
}

We write:

async function foo() {
  return bar();
}

No Script URLs

Script URLs are like eval . They can run JavaScript code that’s in a string.

So instead of writing:

location.href = "javascript:void(0)";

we remove that.

No Self Assignment

We shouldn’t do self-assignment in our code since they’re useless.

For instance, instead of writing:

foo = foo;

or:

[bar, baz] = [bar, abc];

Instead, we write:

foo = bar;
[a, b] = [b, a];

No Self Compare

Self-comparisons are useless, so we shouldn’t write to them.

For instead of writing:

if (x === x) {
  x = 20;
}

We write:

if (x === y) {
  x = 20;
}

No Use of the Comma Operator

We shouldn’t use the comma operator since they always return the last item in the list.

For instance, instead of writing:

const a = (3, 5);

We just write:

const a = 5;

No Returning Values from Setters

We shouldn’t return values from setters since they can’t be used.

For instance, we shouldn’t write:

conat foo = {
  set b(value) {
    this.val = value;
    return value;
  }
};

class Foo {
  set b(value) {
    this.val = value * 2;
    return this.val;
  }
}

Instead, we write:

conat foo = {
  set b(value) {
    this.val = value;
  }
};

class Foo {
  set b(value) {
    this.val = value * 2;
  }
}

Conclusion

We may consider using config files instead of environment variables.

Useless expressions and statements like self compare, returning things in setters, etc. should be removed.