← go back...

Object creation strategies in JavaScript

We have discussed different options to create new objects in an article about Object creation in JavaScript.

In this article we will look at aspects about encapsulation and cardinality that we need to consider while deciding how to create objects.

TL;DR: Use literal object notation for ad-hoc, all public objects. Use a factory function for singleton objects that need private state. Use prototypical inheritance for model objects or types that will need lots of instances.

Literal object by default

Always use literal objects for everything unless you have a compelling reason not to.

This is, hands down, the cheapest and more straightforward syntax when you need objects in JavaScript.

Encapsulation

The downside of literal objects is that everything is public by default and you can't do anything to prevent this.

Not only this breaks encapsulation. It also produces leaky abstractions. You should read about these things if you don't get why they're bad for you.

As we've said before, there is nothing you can do in JavaScript to conceal a property. There is no private keyword as in other languages but we can leverage other language mechanisms to get the same effect and enforce encapsulation.

Enter the function

In JavaScript, functions get their own variable scope. Variables declared inside a function are undefined outside them, but any variable declared outside a function is defined inside:

const a = 33;

function someFn() {
  const b = 42;
  console.log(a);
  console.log(b);
}

someFn(); // outputs "33" and "42"
console.log(b); // outputs "undefined"

In this code, we could say that b in someFn is "private".

Let's say that we have some literal object and we don't like that some of its properties are public:

const a = {
  prop1: 33,
  prop2: 42,
  someMethod: function () {
    console.log(this.prop1, this.prop2);
  }
};

We'd like to expose someMethod and hide prop1 and prop2. We don't want no one to access those properties directly and change their value by accident or couple some other part of the code to them, affecting to our ability to refactor them freely without concerning ourselves about breaking other unexpected places in our code.

We could rewrite it to this:

function A() {
  const prop1 = 33;
  const prop2 = 42;

  return {
    someMethod() {
      console.log(prop1, prop2);
    }
  };
};

const a = A();

This proposes a pattern that is really easy to understand:

We usually write this functions like this:

function A() {
  const prop1 = 33;
  const prop2 = 42;
  const someMethod = () => console.log(prop1, prop2);

  return {someMethod};
};

const a = A();

Cardinality

With literal objects and functions that return objects it seems that we have all our needs covered; we don't need anything more.

This is not true in a browser's context or if efficiency is one of your concerns.

The fact is that we have more ways to create objects in JavaScript. They surely have their purpose.

WIP