JavaScript Best Practices — Improving Classes
Tags: webdev, programming, javascript, codequality
Published: 2021-01-27 02:12:10

**Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62**

**Subscribe to my email list now at http://jauyeung.net/subscribe/**

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.