Categories
JavaScript Best Practices

JavaScript Best Practices — Improving Classes

Cleaning up our JavaScript code is easy with default parameters and property shorthands.

In this article, we’ll look at the best practices for creating classes and when we should create them.

Constructors

There are a few things that we should do to make our constructors better. They are the following.

Initialize All Member Data in All Constructors If Possible

We should put them all in the constructor so that they’re all initialized when we instantiate the object.

So we can write:

class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
}

Now we make sure that everything’s initialized with a value.

Create a Singleton In the Constructor

If we need only one instance of a constructor, then we can create one instance of it.

For instance, we can write the following:

class Person {
  constructor(name) {
    if (this.instance) {
      this.instance = {
        name
      }
    }
    return this.instance;
  }
}

In the code above, we return the object that we created if this.instance isn’t defined yet.

Otherwise, we return whatever it’s set to this.instance .

Prefer Deep Copies to Shallow Copies Until Proven Otherwise

Deep copies copy everything, so that’s a lot better than doing a shallow copy. Shallow copies leave some things referencing the original object.

That’s not good if we want a true copy.

Therefore, we’ve to make our code to make deep copies as follows:

const copy = obj => {
  const copied = {
    ...obj
  };
  for (const k of Object.keys(obj)) {
    if (typeof obj[k] === 'object') {
      copied[k] = {
        ...copied[k]
      };
      copy(copied[k]);
    }
  }
  return copied;
}

We just use the spread operator to copy nested objects if one is found. And do the same thing recursively.

Then we return the object that we copied.

When Should We Create a Class?

We shouldn’t always create classes. There a few scenarios where it makes sense to create a class.

Model Real-World Objects

Classes are great for modeling real-world objects since they model the behavior of objects

They let us encapsulate instance variables and methods into one package to store state and do actions on objects respectively.

Model Abstract Objects

Likewise, we can use classes to model abstract objects.

They can be used to make abstractions, which are generalizations of different kinds of objects.

Classes are great for holding shared members of subclasses. And subclasses can inherit from them.

However, we should keep the inheritance tree simple so that people won’t be confused with the code.

Reduce Complexity

We can use classes to reduce the complexity of a program.

Classes are great for hiding information. In JavaScript, there’re no private variables in classes yet, so we’ve to hide data in methods instead.

We can then minimize coupling between different parts of our program with that.

Hide Implementation Details

Methods are also good for hiding implementation details.

We can hide the details within methods and only run things that are needed.

To do that, we can nest functions and variables inside methods.

Limit Effects of Changes

The effects of changes can be reduced since we can hide things.

As with hiding implementation, the effects of changes can be isolated by limiting the effects of changes within methods.

Hide Global Data

Global data can become private data by putting them inside the methods of a class.

Then they don’t have to be exposed to the public. All we have to do is to use let and const to declare them within methods.

Streamline Parameter Passing

If we have the same parameters passed into different functions, then we can change the parameters to instance variables and the functions to methods.

For instance, if we have:

const speak = (name) => `${name} spoke`;
const greet = (name) => `hi, ${name}`;

Then we can put the methods into their own class as follows:

class Person {
  constructor(name) {
    this.name = name;
  }
  speak() {
    return `${this.name} spoke`;
  }
  greet() {
    return `hi, ${this.name}`;
  }
}

Now we don’t have to pass in name everwhere.

We just make an instance of Person and call those methods without passing in any arguments.

Conclusion

We can create classes to encapsulate data and package things together. However, we shouldn’t create classes for everything.

Also, we should make deep copies rather than shallow copies whenever possible.

Categories
JavaScript Best Practices

JavaScript Best Practices — Classes

Cleaning up our JavaScript code is easy with default parameters and property shorthands.

In this article, we’ll look at the best practices for creating classes.


ES6 Class Basics

To make the prototypical inheritance model of JavaScript less confusing, the class syntax has been introduced in ES6.

It has the constructor , instance variables, and methods just like classes in languages like Java do, but they act differently.

For instance, we can create a class by writing:

We have the name instance variable and the speak method. this is the instance of the class itself.


Getters and Setters

Getters are methods that only return values, whereas setters are methods that let us set a value to an instance variable.

They let us do either in a controlled manner rather than accessing instance variables directly.

We can also add getters and setters to the class as follows:

With these getters and setters, we can write the following to get name:

const person = new Person('joe');
console.log(person.name);

And set name:

const person = new Person('joe');
person.name = 'jane';
console.log(person.name);

Child or Subclasses

We can also create child classes with the extends keyword. For instance, we can write:

We created a Dog class that overrides the speak method of the Animal class.

Also, we call the parent constructor with super in the Dog’s constructor and call super.speak to call the speak method of Animal.

This is much cleaner than the old constructor function syntax for inheritance.


Static Methods and Properties

We can add static properties to a class by using the static keyword.

Doing that will help us add a property to it that’s shared by all instances.

For instance, we can write:

class Dog {
  static type() {
    return 'dog';
  }
}

Then we can call it as a static method as follows:

console.log(Dog.type())

To create static properties, we write the following:

class Dog {}
Dog.type = 'dog';

console.log(Dog.type);

We set the type property to 'dog' and it’s shared across all instances.

This syntax is valid since classes are just functions and functions are treated the same as any other object in JavaScript.


Private Fields

Future versions of JavaScript can also let us define private class fields. There’s no equivalent of this in constructor functions.

They are denoted by the # symbol in front of the field name.


Conclusion

We can define constructors with the class syntax to make them look easier to understand than constructor functions.

The classes look similar to Java classes but act very differently underneath. We have to understand the underlying behavior before we use it. This means that we should understand prototypical inheritance before using JavaScript classes.

Categories
JavaScript Best Practices

JavaScript Best Practices — Promises

Some ways of writing JavaScript code are better than others.

In this article, we’ll look at best practices for using the latest JavaScript promise features.


Converting Non-Promise Async Code to Promises

We can convert the non-promise async code to promises with the util.promisify() method. This lets us convert many functions with async callbacks with the signature of the form (err, res), where err is an object with errors and res has the results of the async code.

Any function that has a promising form defined for it can also use this utility to return the promise form. As long as the promise version is stored as the value of the symbol property util.promisify.custom , we can get the promise version.

For instance, since setTimeout has a promise-based version in the standard library of Node.js, we can convert it to a promise as follows:

const util = require('util');
const sleep = util.promisify(setTimeout);

(async () => {
  await sleep(1000);
  console.log('slept');
})()

Now we have a sleep function that pauses execution for the time we want, instead of having to nest callbacks.


Asynchronous Chaining

In the old way, we chain promises with the then method.

For instance, we write:

promise1
  .then((res) => {
    //...
    return promise2
  })
  .then((res) => {
    //...
    return promise3
  })
  .then((res) => {
    //...
  })

We can clean this up with async and await as follows:

(async () => {
  const val1 = await promise1;
  //...
  const val2 = await promise2;
  //...
  const val3 = await promise3;
  //...
})();

This is much cleaner as the resolve promise values are assigned to the constants or variables on the left side.


Using Catch to Catch Errors

As with synchronous code, we have to catch errors and handle them gracefully.

To do that with promise code, we can call the catch method. For instance:

promise1
  .then((res) => {
    //...
    return promise2
  })
  .then((res) => {
    //...
    return promise3
  })
  .then((res) => {
    //...
  })
  .catch((err) => {
    //...
  })

If we use async and await , then we use try...catch as we do with synchronous code, as follows:

(async () => {
  try {
    const val1 = await promise1;
    //...
    const val2 = await promise2;
    //...
    const val3 = await promise3;
    //...
  } catch (ex) {
    //...
  }
})();

The code in catch in both examples would run when the first promise is rejected.


Use Finally to Run Code That Should Run Regardless of the Outcome of the Promises

If we need to run code that should be run regardless of the outcomes of the promise chain, then we should use the finally method or the finally block for async and await code.

For instance, we can run clean up code in finally as follows:

promise1
  .then((res) => {
    //...
    return promise2
  })
  .then((res) => {
    //...
    return promise3
  })
  .then((res) => {
    //...
  })
  .finally((err) => {
    //...
  })

Or we can write:

(async () => {
  try {
    const val1 = await promise1;
    //...
    const val2 = await promise2;
    //...
    const val3 = await promise3;
    //...
  } catch (ex) {
    console.log(ex);
  } finally {
    //...
  }
})();

Multiple Asynchronous Calls with Promise.all()

If we want to run multiple unrelated promises at once, then we should use Promise.all.

For instance, we can run them all at once as follows:

Promise.all([
    promise1,
    promise2,
    promise3,
  ])
  .then((res) => {
    //...
  })

If we use async and await, we can write:

(async () => {
  const [val1, val2, val3] = await Promise.all([
    promise1,
    promise2,
    promise3,
  ])
})();

In both examples, the resolved values of all three promises are either in res or assigned to an array on the left (in the second example).

This way, they run all at once instead of waiting for each to resolve.


Summary

  • We can clean up our async code by using promises.
  • Converting to async code to promises can be done with Node apps by using the util.promisify method.
  • If we want to chain promises, before sure to add the catch method or block to handle errors gracefully.
  • finally method or block can be used to run code that’s always run regardless of the promises outcomes.
  • Promise.all is great for running unrelated promises all at once.
Categories
JavaScript Best Practices

JavaScript Best Practices — Modern Syntax

There are better ways to write JavaScript code than others.

In this article, we’ll look at the best practices for using the latest JavaScript syntax features.

Syntactic Sugar

In JavaScript, the syntactic sugar that’s included is usually good.

They let us write code in a shorter and clear way and make them more readable in the process.

These syntactic sugars are drop-in replacements for existing techniques so we should use them.

const Isn’t Consistent

const is a keyword for declaring block-scoped constants.

They’ll make primitive values assigned to it immutable since we can’t assign a constant again after it’s assigned to a value.

A value must be assigned when we declare it.

However, const is a bit deceptive since some people might not know that we can still change objects that are assigned to const by changing their properties.

Also, arrays can be changed in place with methods like push and unshift .

Therefore, we shouldn’t assume that objects that are assigned to const are immutable.

Limiting the Scope of the Function

Traditional functions defined with the functionm keyword can be called to run statements defined in the block and may return a value.

They can be run anywhere if they’re written as a function declaration.

For instance, if we have:

function foo() {
  //...
}

Then foo can be run before or after it’s defined.

It also defines its own this and can be used with the new operator as a constructor.

So, to limit the capabilities of our functions, we should use arrow functions.

If we need constructors, then we should use the class syntax to define them to make everyone clear.

The Class Syntax

The class syntax is great for defining constructors. It does the same thing as the old constructor function syntax.

It looks like a class in an object-oriented language like Java, but it does the same thing as JavaScript constructors.

Therefore, the prototypical inheritance model is still used with JavaScript classes.

So the following:

function Person(name) {
  this.name = name;
};

Person.prototype.greet = function() {
  console.log(`hi ${this.name}`);
};

is the same as:

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

  greet() {
    console.log(`hi ${this.name}`);
  }
}

They hold and do the same thing but the placement of the fields and methods are different.

Arrow Functions

Arrow functions are great. They are shorter. Thet but more neatly in callbacks.

It can return without adding the return keyword if we return in the first line.

They encapsulate qualities that make them more convenient. But they aren’t a drop-in replacement for traditional functions defined with the function keyword.

We can call bind by to change this inside a function.

Also, we can’t call call and apply with them to change this and call functions with arguments with them.

Going Asynchronous

Going async with JavaScript has its own difficulty because of JavaScript’s single-threaded nature.

We’ve to write code that unblocks the thread so that they won’t hold up our programs until we’re ready to run them.

This is where async code with callbacks come in to make calling async code easier.

We can make our code async by calling setTimeout . Also, pretty much any HTTP client runs in an async manner.

If we have lots of async code with callbacks, then we have to nest callbacks.

This gets ugly real fast.

If we use callbacks for chaining async code, we may have code that looks like this:

async1((err, res) => {
  if (!err) {
    async2(res, (err, res) => {
      if (!err) {
        async3(res, (err, res) => {
          //...
        });
      }
    });
  }
});

That’s ugly and just not maintainable. Instead, we use promises. Then we can write something like:

promise1
  .then((res) => {
    //...
    return promise2
  })
  .then((res) => {
    //...
    return promise3
  })
  .then((res) => {
    //...
  })

That’s much cleaner than nesting async callbacks with the previous example.

We can make things even shorter by writing:

(async () => {
  const val1 = await promise1;
  //...
  const val2 = await promise2;
  //...
  const val3 = await promise3;
  //...
})();

As we can see, the code is much shorter and it’s exactly the same as the promise chain we have before.

The only difference is that va11 , val2 , and val3 hold the resolved values of the promises instead of res .

Conclusion

We got to know some things about JavaScript like how const aren’t always immutable, and clean up async code with async and await.

Also, arrow functions and classes should be used for regular functions and constructors respectively.

Categories
JavaScript Best Practices

Performant JavaScript Best Practices – Reduce dependencies and DOM interactions

Like any program, JavaScript programs can get slow fast if we aren’t careful with writing our code.

In this article, we’ll look at some best practices for writing fast JavaScript programs.

Reduce DOM Manipulation With Host Object and User’s Browser

DOM manipulation is slow. The more we do, the slower it’ll be. Since DOM manipulation is synchronous, each action is done one at a time, holding the rest of our program.

Therefore, we should minimize the number of DOM manipulation actions that we’re doing.

The DOM can be blocked by loading CSS and JavaScript. However, images aren’t blocking render so that they don’t hold up our page from finishing loading.

However, we still want to minimize the size of our images.

Render blocking JavaScript code can be detected with Google PageSpeed Insights, which tells us how many pieces of render-blocking JavaScript code we have.

Any inline CSS would block the rendering of the whole page. They are the styles that are scattered within our page with style attributes.

We should move them all to their own style sheets, inside the style tag, and below the body element.

CSS should be concatenated and minified to reduce the number of the stylesheet to load and their size.

We can also mark link tags as non-render blocking by using media queries. For instance, we can write the following to do that:

<link href="portrait.css" rel="stylesheet" media="orientation:portrait">

so that it only loads when the page is displayed in portrait orientation.

We should move style manipulation outside of our JavaScript and put styles inside our CSS by putting styles within their own class in a stylesheet file.

For instance, we can write the following code to add a class in our CSS file:

.highlight {  
  background-color: red;  
}

and then we can add a class with the classList object as follows:

const p = document.querySelector('p');  
p.classList.add('highlight');

We set the p element DOM object to its own constant so we can cache it and reuse it anywhere and then we call classList.add to add the hightlight class to it.

We can also remove it if we no longer want it. This way, we don’t have to do a lot of unnecessary DOM manipulation operations in our JavaScript code.

If we have scripts that no other script depends on, we can load then asynchronously so that they don’t block the loading of other scripts.

We just put async in our script tag so that we can load our script asynchronously as follows:

<script async src="script.js"></script>

Now script.js will load in the background instead of in the foreground.

We can also defer the loading of scripts by using the defer directive. However, it guarantees that the script in the order they were specified on the page.

This is a better choice if we want our scripts to load one after another without blocking the loading of other things.

Minifying scripts is also a must-do task before putting our code into production. To do that, we use module bundlers like Webpack and Parcel, which so create a project and then build them for us automatically.

Also, command-line tools for frameworks like Vue and Angular also do code minification automatically.

Minimize the Number of Dependencies Our App Uses

We should minimize the number of scripts and libraries that we use. Unused dependencies should also be removed.

For instance, if we’re using Lodash methods for array manipulation, then we can replace them with native JavaScript array methods, which are just as good.

Once we remove our dependency, we should remove them from package.json and the run npm prune to remove the dependency from our system.

Poor Event Handling

Event handling code is always slow if they’re complex. We can improve performance by reducing the depth of our call stack.

That means we call as few functions as possible. Put everything in CSS style sheets if possible if we’re manipulating styles in our event handlers.

And do everything to reduce calling functions like using the ** operator instead of calling Math.pow .

Conclusion

We should reduce the number of dependencies and loading them in an async manner if possible.

Also, we should reduce the CSS in our code and move them to their own stylesheets.

We can also add media queries so that stylesheets don’t load everywhere.

Finally, we should reduce the number of functions that are called in our code.