JavaScript Best Practices for Creating Objects

John Au-Yeung - Jan 27 '21 - - Dev Community

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/

JavaScript is an easy programming language to learn. It’s easy to write programs that run and do something. However, it’s hard to account for all the use cases and write robust JavaScript code.

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


Use Factory Functions

Factory functions are functions that return a new instance of a constructor or class.

We can use them to create objects without writing any code with the new keyword to instantiate classes or constructors.

Constructors are often a confusing part of JavaScript and definitely one we can avoid if we wish to.

For instance, we can make our own factory function as follows:

In the code above, we have the createPerson factory function that takes a firstName and lastName parameter and returns an object with the firstName, lastName, and the fullName method.

We used an arrow function so that we don’t get confused with the value of this that’s in the returned object. Since arrow functions don’t bind to this, we know that the value of this in fullName is the returned object.

Factory functions can return objects with anything, so the possibilities are endless.

To use our createPerson factory function, we can write the following code:

const person = createPerson('Jane', 'Smith');
const fullName = person.fullName();
Enter fullscreen mode Exit fullscreen mode

We created a person object with the createPerson function and then called the fullName method on it.

Then we get that fullName is ‘Jane Smith’ since this references the person object.

Many JavaScript functions like Number and Boolean are factory functions. They take any object as an argument and then return a number or boolean, respectively.

It makes our JavaScript code more robust because we don’t have to worry about class or constructor instances. We just have to think in terms of objects and composing functions.

Composing functions makes code more reusable and testable, creating more robust code because of this.


Create Instance Methods for Constructors by Attaching to the prototype Property

When we want to create instance methods of a constructor, we should attach the methods to the prototype property of the constructor.

This way, when we instantiate the constructor, we can also call the methods on the instance.

A method that’s attached directly to the function is a static method that’s shared by all methods.

For instance, we can add instance methods to a constructor by attaching it to its prototype property as follows:

In the code above, we created a Fruit constructor that returns a Fruit instance. Then we added the grow instance method by writing:

Fruit.prototype.grow = function() {
  console.log(`${this.name} grew.`);
}
Enter fullscreen mode Exit fullscreen mode

Then when we instantiate it as follows:

const fruit = new Fruit('apple');
fruit.grow();
Enter fullscreen mode Exit fullscreen mode

Instance methods encapsulate methods inside the constructor instance, preventing it from being exposed to outside code and allowing it to make accidental changes.

Therefore, this makes our code more robust. Also, every constructor has its own responsibility since it has its own instance method. This prevents multiple constructors from doing different things to the same object.

The alternative (above) creates a copy of the grow instance method for each instance. The method isn’t cached, so it’s slower to use attach instance methods this way.


Use the .type Property to Create Factory Functions That Can Create Multiple Types of Objects

Since factory functions can be composed, we can create a factory function that can create multiple types of objects.

We can differentiate between them with the type property, which is a conventional way to distinguish between different types of objects that we want to create.

To do this, we can write the following code:

In the code above, we have the createPet factory function that calls the createDog or createCat factory function depending on what kind of pet type we pass into createPet.

We also composed factory functions by calling a factory function inside the createPet factory function.

Now we just have to worry about one factory function (createPet) instead of using multiple factory functions to create different kinds of objects. This hides the complexity that’s underneath the code and thus there’s less risk of breaking things when we make changes.


Conclusion

With factory functions, we can create them to create new objects. We can also compose them and call different ones within one factory function to do different things.

When we have a constructor function, we should attach instance methods to the prototype property of the constructor function so that they don’t have to be recreated all the time.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .