Categories
JavaScript Best Practices

JavaScript Best Practices — Useless Code and Dangerous 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 Unnecessary Boolean Casts

We shouldn’t have duplicate boolean casts.

For instance, we shouldn’t have code like:

const foo = !!!bar;

const foo = !!bar ? baz : bat;

const foo = Boolean(!!bar);

const foo = new Boolean(!!bar);

if (!!foo) {
  // ...
}

if (Boolean(foo)) {
  // ...
}

while (!!foo) {
  // ...
}

Instead, we write:

const foo = !bar;

const foo = bar ? baz : bat;

const foo = Boolean(bar);

const foo = new Boolean(bar);

if (foo) {
  // ...
}

if (foo) {
  // ...
}

while (foo) {
  // ...
}

We remove all the extra Boolean or ! calls and operators since we don’t need them.

No Unnecessary Labels

We can put labels to label loops.

But most of the time, we don’t need them.

It’s also a rarely used feature.

So many people will be confused.

For instance, instead of writing:

A: while (a) {
  break A;
}

We write:

while (a) {
  break;
}

No Unnecessary Parentheses

We should have parentheses in places where they’re unnecessary.

For instance, instead of writing:

a = (b * c);

(a * b) + c;

for (a in (b));

for (a of (b));

typeof (a);

We write:

a = b * c;

a * b + c;

for (a in b);

for (a of b);

typeof a;

This saves us typing and space.

No Unnecessary Semicolons

We shouldn’t add unnecessary semicolons.

For instance, instead of writing:

const x = 5;;

We write:

const x = 5;

We also don’t need semicolons after function declarations:

function foo() {
  //...
};

We can just write:

function foo() {
  //...
}

No Case Statement Fallthrough

Remember to add break in case statements.

For instance, we shouldn’t write:

switch (foo) {
  case 1:
    doSomething();

  case 2:
    doSomethingElse();
}

Instead, we write:

switch (foo) {
  case 1:
    doSomething();
    break;
  case 2:
    doSomethingElse();
    break;
}

No Floating Decimals

Floating decimals are confusing for some people.

We can write a decimal in place of 0.

For instance,

const num = .5;

sets num to 0.5.

We should just put the leading zero in our number:

const num = 0.5;

No Reassigning Function Declarations

We shouldn’t reassign function declarations to something.

If we do, it’ll cause confusion.

For instance, instead of writing:

function foo() {}
foo = bar;

We write:

function foo() {}
const bar = 1;

No Assignment to Native Objects or Read-Only Global Variables

We shouldn’t assign anything to native objects or read-only global variables.

For instance, we shouldn’t have code like:

window = {};
`Object = undefined;
undefined = 1;`

Instead, we assign them to our own variables:

const a = {};
const b = undefined;
const c = 1;

Type Conversion with Shorter Notations

Some shorter notations can be used.

For instance, we can write:

const num = +'123';

to convert num to a number.

We can also use !! to convert to a boolean:

const b = !!foo;

Others like concatenating to a string or using bitwise operators are more confusing and probably should be avoided.

No Declarations in the Global Scope

We shouldn’t declare global variables.

It’s easy to have scope clashes and it’s hard to trace where they came from.

For instance, we shouldn’t have code like:

var foo = 1;
bar = 2;
function baz() {}

Instead, we write:

let foo = 1;
const bar = 2;
let baz = () => {}

We keep them block-scoped to prevent accidental access.

No Implied eval()

setTimeout and setInterval can take a string instead of a callback to run code.

It’s like eval . The code is dangerous and prevents optimizations since the code is in a string.

So instead of writing:

setTimeout("alert('hello');", 100);

setInterval("alert('hello');", 100);

We write:

setTimeout(function() {
  alert("hello");
}, 100);

setInterval(function() {
  alert("hello");
}, 100);

Conclusion

We should always pass in a callback instead of a string to setTimeout and setInterval .

Also, we shouldn’t case booleans unnecessarily.

Useless labels and parentheses should also be removed.

And we should never create global variables or assign things to built-in objects.

Categories
JavaScript Best Practices

JavaScript Best Practices — React Components 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.

Prefer await to then() for reading Promise values

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;
}

Now we have no more callbacks.

Ensures the Proper Number of Arguments are Passed to Promise Functions

We should pass in the proper argument to promise methods.

Promise.all is called with an array of promises.

Same with Promise.race .

Promise.resolve is called with the resolved value.

Promise.reject is called with the reason for rejection.

then is called a success and optionally and failure callback.

catch is called with a callback.

finally is the same as catch .

Quotes Around Object Literal Property Names

If a property name isn’t a valid identifier, then we need quote around then.

For instance, we write:

const obj = {
  "foo bar": true
};

Otherwise, we don’t need the quotes:

const obj = {
  foo: true
};

Consistent Use of Either Backticks, Double, or Single quotes

If we pick one string delimiter to use everywhere, it would be the backtick.

We can use template strings whether we need to interpolate expressions or not.

For instance, we can write:

const backtick = `backtick`;

or:

const greet = `hello ${name}`;

We can even write multiline string with them:

const backtick = `foo
bar`;

If there’s a linebreak in the string, then it’ll be rendered as such.

Require Radix Parameter

We should puyt the radix argument in the parseInt method call to make sure we parse the number with the right base.

For instance, the following:

const num = parseInt("091");

may be interpreted as an octal number.

Instead, we should write:

const num = parseInt("091", 10);

to convert it as a decimal number.

Enforces Consistent Naming for Boolean Props

In React, we shoild make sure boolean props have a consistent naming scheme to reduce confusion.

For instance, instead of writing:

import PropTypes from 'prop-types';

class Heading extends React.Component {
  render() {
    return (
      <h1>{this.props.enabled}</h1>
    );
  }
}

Heading.propTypes = {
  enabled: PropTypes.boolean
};

We write:

import PropTypes from 'prop-types';

class Heading extends React.Component {
  render() {
    return (
      <h1>{this.props.enabled}</h1>
    );
  }
}

Heading.propTypes = {
  isEnabled: PropTypes.boolean
};

The is prefix tells us it’s a boolean prop easily.

Don’t Use button Elements without an Explicit Type Attribute

We shouldn’t use the button element without an explicit type attribute.

Otherwise, we may get unexpected results.

For instance, instead of writing:

const Foo = <button>Foo</button>

We write:

const Foo = <button type='button'>Foo</button>

Default Props should have a Corresponding Non-required PropType

We should have a default prop for non-rrquire prop types.

This way, the props are never undefined .

For instance, insteaf of writing:

function FooBar({ foo, bar }) {
  return <div>{foo}{bar}</div>;
}

FooBar.propTypes = {
  foo: React.PropTypes.string.isRequired,
  bar: React.PropTypes.string
};

FooBar.defaultProps = {
  foo: "foo"
};

We write:

function FooBar({ foo, bar }) {
  return <div>{foo}{bar}</div>;
}

FooBar.propTypes = {
  foo: React.PropTypes.string.isRequired,
  bar: React.PropTypes.string
};

FooBar.defaultProps = {
  bar: "bar"
};

Enforce Consistent Usage of Destructuring Assignment of Props, State, and Context

We should use the destructuring assignment of props, state, and context consistent.

For instance, we should write:

const IdComponent = ({id}) => {
  return (<div id={id} />)
};

or:

const Foo = class extends React.PureComponent {
  render() {
    const { name } = this.state;
    return <div>{name}</div>;
  }
};

Add displayName in a React component Definition

We should set the displayNamne property so that we see the name when we debug with React dev tools.

For instance, we write:

export default function Greet({ name }) {
  return <div>hello {name}</div>;
}
Greet.displayName = 'Greet';

Conclusion

We sure make sure we define and use promises properly.

Also, when we create React components, we should have some prop validations and default props for optional ones.

Also, displayName is useful for debugging.

Categories
JavaScript Best Practices

JavaScript Best Practices — Paths, Numbers, and Operands

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 Negating the Left Operand in in Expressions

Forgetting to negate the whole in expression is a mistake.

Negating the left operand will negate nothing else.

For instance, don’t write:

if (!key in object) {
  //...
}

We write:

if (!(key in object)) {
  //...
}

to negate the whole expression.

No Nested Ternary Expressions

Nested ternary expressions are hard to read, so we shouldn’t use them/

For instance, we shouldn’t write:

const foo = bar ? baz : qux === quxx ? abc : baz;

We should just write if statements to replace it:

if (foo) {
  thing = bar;
} else if (baz === qux) {
  thing = abc;
} else {
  thing = baz;
}

No Using new For Side Effects

We shouldn’t use new for side effects.

So we shouldn’t write:

new Animal();

Instead, we write:

const animal = new Animal();

We assign the returned value to a variable so we can use it.

No Function Constructor

The Function constructor takes string arguments for creating functions.

It’s hard to optimize the code because of that, and we don’t want to let anyone pass strings to it.

Instead of writing:

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

We write:

const x = (a, b) => {
  return a + b;
};

No Object Constructors

Object constructors shouldn’t be used.

They don’t bring any benefit and it’s longer to write.

For instance, instead of writing:

const obj = new Object();

We write:

const obj = {};

No new require

We shouldn’t use new and require together.

Instead, we separate them to remove confusion between:

const foo = new require('foo');

and

const foo= new require('foo');

So instead of writing those, we write:

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

No Symbol Constructor

The Symbol function isn’t a constructor.

We call it directly to create a symbol.

So instead of writing:

const foo = new Symbol("foo");

We write:

const foo = ymbol("foo");

No Primitive Wrapper Instances

We shouldn’t use primitive wrappers to create primitive values.

They have the object types instead of the usual primitive types.

There’s no benefit to using them.

If primitive values need to be wrapped into objects, then it’ll be done temporarily automatically.

So instead of writing:

const stringObject = new String("hello");

We write:

const str = "hello";

No Calling Global Object Properties as Functions

We shouldn’t call global object properties as functions since they aren’t functions.

For instance, we shouldn’t have code like:

const math = Math();

const newMath = new Math();

const json = JSON();

const newJSON = new JSON();

const reflect = Reflect();

Instead, use their properties:

const obj = JSON.parse("{}");

const value = Reflect.get({ x: 1, y: 2 }, "x");

const pi =  Math.PI;

No Octal Literals

We shouldn’t have numbers that start with a zero.

They’re treated as octal literals.

For instance, instead of writing:

const num = 071;

is actually 57 in decimal.

So we should write:

const num = 71;

instead.

No Octal Escape Sequences in String Literals

Octal escape sequences in string literals are deprecated in ES5, so they shouldn’t be used.

So we shouldn’t write:

const foo = "hello \251";

Instead, we write:

const foo = "hello \xA9";

No Reassignment of Function Parameters

We shouldn’t reassign function parameters to a new value.

They mutate the argumenrts objects and also the variable itself if they’re objects.

So instead of writing;

function foo(bar) {
  bar = 100;
}

We write:

function foo(bar) {
  let baz = 13;
}

No String Concatenation when Using __dirname and __filename

We shouldn’t concatenate those expressions with the rest of the path since they don’t work together properly with all platforms.

So instead of writing:

const fullPath = __dirname + "/foo.js";

const filePath = __filename + "/foo.js";

We write:

const fullPath = path.join(__dirname, "foo.js");

const fullPath = path.join(__filename, "foo.js");

Conclusion

We shouldn’t concatenate paths together.

Instead, we use the path module methods to do so.

Nested ternary expressions are hard to read.

Deprecated expressions should be removed.

And never use octal literals

Categories
JavaScript Best Practices

JavaScript Best Practices — Conditionals, Classes, and Over-Optimization

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.

Avoid Conditionals

We can avoid conditionals and replace it with polymorphism.

For instance, instead of writing:

class Person {
  // ...
  getData() {
    switch (this.type) {
      case "student":
        return this.getStudentData();
      case "employee":
        return this.getEmployeeData();
      case "manager":
        return this.getManagerData();
    }
  }
}

We can split them into multiple subclasses:

class Person {
  // ...
}

class Student extends Person {
  // ...
  getData() {
    return this.getStudentData();
  }
}

class Employee extends Person {
  // ...
  getData() {
    return this.getEmployeeData();
  }
}

class Manager extends Person  {
  // ...
  getData() {
    return this.getManagerData();
  }
}

Instead of a big class that does multiple things, we split them into multiple subclasses.

The shared code is in Person .

Thes rest is in the methods exclusive to the subclasses.

this.getStudentData, this.getEmployeeData, and this.getManagerData can stay in the subclasses.

Don’t Over-Optimize

We shouldn’t over-optimize our code if we have to sacrifice readability.

For instance, we don’t have to cache loop lengths.

It won’t bring much difference in performance in modern browsers, but we make our code harder to read.

Instead of writing:

for (let i = 0, len = items.length; i < len; i++) {
  // ...
}

We write:

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

or just use the for-of loop:

for (const item of items) {
  // ...
}

Remove Dead Code

We should remove dead code since they aren’t used.

For instance, instead of writing

const oldRequest = (params) => {
  //...
}

const newRequest = (params) => {
  //...
}

newRequest({
  //...
});

We write:

const newRequest = (params) => {
  //...
}

newRequest({
  //...
});

Use Getters and Setters

We should use getters and stress on objects.

They let us do validation before getter and setting data.

And we can encapsulate any logic that’s run before getting.

Logging and error handling is easier, and we can lazy load our stuff in getters.

For instance, instead of writing:

function makeCart() {
  // ...

  return {
    cart: []
    // ...
  };
}

We write:

function makeCart() {
  let cart = [];

  function getCart() {
    return cart;
  }

  function setCart(newCart) {
    cart = newCart;
  }

  return {
    // ...
    getCart,
    setCart
  };
}

We create a cart with a private cart and a getCart getter function and setCart setter function.

We expose what we want in our code.

Now we can use it by writing:

const cart = makeCart();
cart.setBalance(cartItems);

Make Objects have Private Members

We can use closures to keep private members in objects.

For instance, instead of writing:

const person = {
  name: 'james',
  age: 20
}

We write:

const person = (() => {
  const age = 20;
  return {
    name: 'james'
  }
})();

We return an object with the name but not the age .

That’s hidden in the function.

This one of the ways to keep private variables.

Module members are also private unless we export them.

Prefer Class Syntax Over Constructor Functions

The constructor function makes inheritance harder.

To do inheritance, we’ve to inherit the methods and add instance methods to the prototype of the constructor.

For instance, we write:

const Animal = function(name) {
  this.name = name;
};

Animal.prototype.move = function() {};

const Dog = function(name) {
  Animal.call(this, name);
  this.age = age;
};

Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.bark = function() {};

We’ve to create the Animal constructor and add instance methods to its prototype.

Then we create the Dog constructor and call the Animal constructor with the arguments.

Then we inherited the methods from the Animal ‘s prototype with Object.create .

And then we set the constructor property of its Prototype to itself to get the right result when we use instanceof .

Then we add instance methods exclusive to Dog .

With the class syntax, it’s much easier.

For instance, we write:

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

  move() {
    /* ... */
  }
}

class Dog extends Animal {
  constructor(age) {
    super(name);
    this.age = age;
  }

  bark() {
    /* ... */
  }
}

We put the constructor and methods all inside the class.

We call the parent constructor with super ,

And we extends to indicate inheritance.

Conclusion

The class syntax makes inheritance much easier.

Conditionals can be replaced with polymorphism.

If optimization doesn’t bring much benefit and make our code harder to read, then don’t do it.

Categories
JavaScript Best Practices

JavaScript Best Practices — Removing Duplicates

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 Duplicate Keys in Object Literals

We shouldn’t have duplicate keys in object literals.

This will yield unexpected results.

For example, we shouldn’t write:

const user = {
  name: 'james',
  name: 'joe'
}

Instead, we should remove them or name them differently.

No Duplicate case Labels in switch Statements

We should’t have duplicate case labels in our switch statements.

For example, we shouldn’t write:

switch (id) {
  case 1:
    // ...
  case 1:
    //...
}

Only the first one that matches the id value will run.

Therefore, we should combine them or remove them.

Use Single import Per Module

There should only be one import statemen per module in our code.

For example, we shouldn’t have:

import { f1 } from 'module'
import { f2 } from 'module'

Instead, we write:

import { f1, f2 } from 'module'

We save space and remove duplicates.

No Empty Character Classes in Regexes

Our regexes shouldn’t have empty character classes in them.

It’s probably a mistake to have them.

For instance, we shouldn’t write:

const myRegex = /^abc[]/;

No Empty Destructuring Patterns

If we have empty destructuring patterns, it’s probably a mistake.

Also, we probably meant to assign a default value to an empty object to a variable.

For example, we shouldn’t write:

const { a: {} } = foo

Instead, we write:

const { a = {} } = foo

which will assign an empty object to a if foo.a is undefined .

Or we can write;

const { a: { b } } = foo;

to assign something nested in a to another variable.

Don’t Use eval

eval lets us run code from a string.

This is a security risk since we can run any code inside a string.

Also, it prevents the JavaScript interpreter from optimizing our code.

Therefore, we should never use the eval function.

No Reassigning Exceptions in catch clauses

We should never reassign exceptions in catch clauses.

This will discard the original exception data.

For example, we shouldn’t write;

try {
  // ...
} catch (e) {
  e = 'value'
}

Instead, we write:

try {
  // ...
} catch (e) {
  const newVal = 'value'
}

Assigning our value to a new variable is much better than assigning something to the exception object.

Don’t Extend Native Objects

Native objects aren’t meant to be extended.

Therefore, we should just what’s built-in.

Having extra properties in the native objects confuses people and they may be surprised if they don’t exist anywhere else.

For example, we should have code like:

Object.prototype.age = 2

No Unnecessary Function Binding

We only need to call bind on a function if we need to change the value of this inside the function.

Therefore, we shouldn’t call it if we don’t need to do that.

So we shouldn’t write:

const name = function () {
  getName()
}.bind(user)

Instead, we write:

const name = function () {
  this.getName()
}.bind(user)

No Unnecessary Boolean Casts

If we have a boolean already, then we shouldn’t have to cast them.

For example, if we have:

const result = true;

Then we shouldn’t write:

if (!!result) {
  // ...
}

We can just use it as-is:

if (result) {
  // ...
}

No Unnecessary Parentheses Around Function Expressions

We don’t need to wrap a function around parentheses.

For instance, we shouldn’t have code like:

const foo = (function () { })

We don’t need the parentheses around the function expression.

Instead, we write:

const foo = function () { }

Use break to Prevent Fallthrough in switch Cases

We should always add break at the end of each case block or statement so that the code below it won’t get executed.

For instance, instead of writing:

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

We write:

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

Conclusion

If we have bad code like missing break statements, we should add them.

If we have redundant operations like converting a boolean to a boolean, then we should remove them.

Duplicates members and variables should also be eliminated.