Categories
JavaScript Best Practices

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

Spread the love

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.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *