Categories
JavaScript Best Practices

JavaScript Best Practices — Constructors and Classes

Spread the love

JavaScript lets us do a lot of things. It’s sometimes too forgiving in its syntax.

In this article, we’ll look at some best practices when developing in JavaScript, including object creation and constructors.

Prototypes

When we have methods in our constructor, we should add them to the prototype property of the constructor instead of putting it inside the constructor function.

For instance, instead of writing:

function Person(name) {
  this.name = name;
  this.greet = function() {
    return `hi ${this.name}`;
  }
}

We should write:

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

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

Even better, we use the class syntax by writing:

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

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

The last 2 pieces of code are the same.

We want to write our constructors like the last 2 examples because we only want to share one copy of the greet method between all the instances.

The greet method doesn’t change, only the value of this does.

Therefore, we don’t want to keep different copies of the same method as we would do in the first example.

Instead, we want to share it as we do in the last 2 examples.

We can create an instance of the constructor to find the difference:

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

If we log the person object, we can see that the method is in the __proto__ property, which is the prototype of the person .

It has the greet method.

If we create the same object with the version first version of the Person constructor, we’ll see that the greet method is in the person object itself.

Therefore, to save memory, the method should be in the constructor’s prototype property rather than inside the constructor itself.

Constructor’s Return Values

Constructors don’t have to return something explicitly.

We usually shouldn’t return anything explicitly because it’ll return the instance of the constructor automatically.

However, it does let us return something different if we want.

But we usually shouldn’t do that unless we have a reason to.

If we want to do that we can write:

function Person(name) {
  this.name = name;
  return {
    foo: name
  }
}

Then when we instantiate it as follows:

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

We get {foo: “joe”} instead of Person {name: “joe”} according to the console log output.

Patterns for Enforcing new

To create a new instance of an object, we need to use new .

The best way to do that is to use the class syntax.

This way we can’t call it without new .

If we call a constructor without new , then this would be the global object.

Naming Convention for Constructors/Classes

Constructors or classes are usually named in PascalCase.

This distinguishes regular functions from constructors and classes.

Using that

If we want to make sure that our constructor always behaves as a constructor, we can also create an object and return that.

For instance, we can write:

function Person(name) {
  const that = {};
  that.name = name;
  return that;
}

Now if we call our Person constructor with our without new , we still get an object returned.

Self-Invoking Constructor

We may also use the instanceof operator to check if a constructor’s this is the instance of the constructor.

Then we’ll know if we called it with new or not.

If we did, then the constructor’s this should be an instance of the constructor.

For instance, we can check as follows:

function Person(name) {
  this.name = name;
  if (!(this instanceof Person)) {
    return new Person(name);
  }
}

Now whether we called the Person constructor with new or not, we get the same result.

const person = new Person('joe');

and:

const person = Person('joe');

should get us the same result.

Conclusion

We should keep our methods in the prototype of the constructor rather than inside the constructor itself.

This way, one copy of the method is shared among all the instances.

This works because the method is the same across all instances.

The class syntax does this for us automatically.

Also, we can check if this is an instance of the constructor to check if our constructor has been called properly.

The class syntax also takes care of that for us since we can’t invoke a class without the new keyword.

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 *