JavaScript
Article

JavaScript Object Creation: Patterns and Best Practices

By Jeff Mott

This article was peer reviewed by Tim Severien and Sebastian Seitz. Thanks to all of SitePoint’s peer reviewers for making SitePoint content the best it can be!

JavaScript object creation is a tricky subject. The language has a multitude of styles for creating objects, and newcomers and veterans alike can feel overwhelmed by the choices and unsure which they should use. But despite the variety and how different the syntax for each may look, they’re more similar than you probably realize. In this article, I’m going to take you on a tour of the various styles of object creation and how each builds on the others in incremental steps.

Object Literals

The first stop on our tour is the absolute simplest way to create objects, the object literal. JavaScript touts that objects can be created “ex nilo”, out of nothing—no class, no template, no prototype—just poof, an object with methods and data.

var o = {
  x: 42,
  y: 3.14,
  f: function() {},
  g: function() {}
};

But there’s a drawback. If we need to create the same type of object in other places, then we’ll end up copy-pasting the object’s methods, data, and initialization. We need a way to create not just the one object, but a family of objects.

Factory Functions

The next stop on our tour is the factory function. This is the absolute simplest way to create a family of objects that share the same structure, interface, and implementation. Rather than creating an object literal directly, instead we return an object literal from a function. This way, if we need to create the same type of object multiple times or in multiple places, we only need to invoke a function.

function thing() {
  return {
    x: 42,
    y: 3.14,
    f: function() {},
    g: function() {}
  };
}

var o = thing();

But there’s a drawback. This approach can cause memory bloat because each object contains its own unique copy of each function. Ideally we want every object to share just one copy of its functions.

Prototype Chains

JavaScript gives us a built-in mechanism to share data across objects, called the prototype chain. When we access a property on an object, it can fulfill that request by delegating to some other object. We can use that and change our factory function so that each object it creates contains only the data unique to that particular object, and delegate all other property requests to a single, shared object.

var thingPrototype = {
  f: function() {},
  g: function() {}
};

function thing() {
  var o = Object.create(thingPrototype);

  o.x = 42;
  o.y = 3.14;

  return o;
}

var o = thing();

In fact, this is such a common pattern that the language has built-in support for it. We don’t need to create our own shared object (the prototype object). Instead, a prototype object is created for us automatically alongside every function, and we can put our shared data there.

thing.prototype.f = function() {};
thing.prototype.g = function() {};

function thing() {
  var o = Object.create(thing.prototype);

  o.x = 42;
  o.y = 3.14;

  return o;
}

var o = thing();

But there’s a drawback. This is going to result in some repetition. The first and last lines of the “thing” function are going to be repeated almost verbatim in every such delegating-to-prototype-factory-function.

ES5 Classes

We can isolate the repetitive lines by moving them into their own function. This function would create an object that delegates to some other arbitrary function’s prototype, then invoke that function with the newly created object as an argument, and finally return the object.

function create(fn) {
  var o = Object.create(fn.prototype);

  fn.call(o);

  return o;
}

// ...

Thing.prototype.f = function() {};
Thing.prototype.g = function() {};

function Thing() {
  this.x = 42;
  this.y = 3.14;
}

var o = create(Thing);

In fact, this too is such a common pattern that the language has some built-in support for it. The “create” function we defined is actually a rudimentary version of the “new” keyword, and we can drop-in replace “create” with “new”.

Thing.prototype.f = function() {};
Thing.prototype.g = function() {};

function Thing() {
  this.x = 42;
  this.y = 3.14;
}

var o = new Thing();

We’ve now arrived at what we commonly call ES5 classes. They are object creation functions that delegate shared data to a prototype object and rely on the “new” keyword to handle repetitive logic.

But there’s a drawback. It’s verbose and ugly, and implementing inheritance is even more verbose and ugly.

ES6 Classes

A relatively recent addition to JavaScript is ES6 classes, which offer a significantly cleaner syntax for doing the same thing.

class Thing {
  constructor() {
    this.x = 42;
    this.y = 3.14;
  }

  f() {}
  g() {}
}

var o = new Thing();

Comparison

Over the years, we JavaScripters have had an on-and-off relationship with the prototype chain, and today the two most common styles you’re likely to encounter are the class syntax, which relies heavily on the prototype chain, and the factory function syntax, which typically doesn’t rely on the prototype chain at all. The two styles differ—but only slightly—in performance and features.

Performance

JavaScript engines are so heavily optimized today that it’s nearly impossible to look at our code and reason about what will be faster. Measurement is crucial. Yet sometimes even measurement can fail us. Typically an updated JavaScript engine is released every six weeks, sometimes with significant changes in performance, and any measurements we had previously taken, and any decisions we made based on those measurements, go right out the window. So, my rule of thumb has been to favor the most official and most widely used syntax, under the presumption that it will receive the most scrutiny and be the most performant most of the time. Right now that’s the class syntax, and as I write this, the class syntax is roughly 3x faster than a factory function returning a literal.

Features

What few feature differences there were between classes and factory functions evaporated with ES6. Today, both factory functions and classes can enforce truly private data—factory functions with closures and classes with weak maps. Both can achieve multiple inheritance—factory functions by mixing other properties into its own object, and classes also by mixing other properties into its prototype, or with class factories, or with proxies. Both factory functions and classes can return any arbitrary object if need be. And both offer a simple syntax.

Conclusion

All things considered, my preference is for the class syntax. It’s standard, it’s simple and clean, it’s fast, and it provides every feature that once upon a time only factories could deliver.

  • Camilo Reyes

    Nice article. Up until now I’ve avoided the class syntax, to be honest. My complaint is it doesn’t hoist like functions. I do like your take on this.

  • Tuggyukki

    Constructor functions + new were in Javascript from the beginning, so maybe “pre-ES6 class” might be a better name for it.Of course it’s still not a real class, javascript is a classless language. Even ES6 class is just a syntactic sugar on top of the prototypical nature of the language.

  • dr3am3r

    I’d also mention Reflect.construct

  • http://www.nodewiz.biz/ Christopher Langton

    Until you consider scope, i.e. the value of ‘this’ the whole article seems like a flippy joke.
    Go read Crockford’s explanation on Prototypal Inheritance and how classical OOP is flawed in JavaScript so you learn from the foremost expert then edit this article to be an actual resource for beginners without being so vague and dangerous.

    • http://www.skooppa.com s.molinari

      If Jeff had of spoken of scope or mentioned the issues of OOP in Javascript, would that have really changed the advantages or disadvantages or the different object instantiation methods in JS, as presented in this post, one bit? I think not. Would it mean he or anyone else should change their mind about how to best instantiate objects? I think not.

      • http://www.nodewiz.biz/ Christopher Langton

        Have you ever written JavaScript in a team? I think not. Have you ever dug into the most successful JavaScript library (jQuery) and learned first hand best practice for code used as is in all types of teams and implementations? I think not.

        The ignorance to the obvious is probably my own fault for not writing my comment like an educational example to ensure you understood. But the fact im a commenter on an article claiming to do just that concisely demonstrates my point, or you would not be making the arguments you are now

  • http://www.nodewiz.biz/ Christopher Langton

    Advantages and disadvantages are not explained, they are fuzzy at best. If you really think this is great then after you learn from Douglas Crockford you’ll feel like Neo in the matrix when he see’s the real world for the first time.
    Right now, your more like Cypher happy in blissful ignorance.

    • http://www.skooppa.com s.molinari

      In each section explaining each of the object instantiation methods in JS, Jeff explains what is good about the method, then says, “but there is a/are drawback/s”. That looks like advantages and disadvantages to me. Clearly you can’t be going into “The Matrix” kind of detail in a single blog post. It isn’t really a tutorial.

      Douglas might be a great teacher too, but that is absolutely no reason to belittle this article or my post. It is a good article and my post says the truth about it.

      • http://www.nodewiz.biz/ Christopher Langton

        Oh yes i see, there are drawbacks. Clear (as mud). You make my point for me.

        There is no doubt effort and skill are behind the article but “belittle”? No, if an intelligent person claims criticism is anything but honest alternative opinions to learn from, then i’d have to question said persons intelligence or life experience. Jeff doesnt take criticism as belittlement.

  • http://www.nodewiz.biz/ Christopher Langton

    Yet another argument from you lacking substance, If the challenge I presented for you to learn from Douglas Crockford doesnt satisfy a constructive comment, it certainly meets your last statement. perhaps before rage replying to someone with different views you might actually read and absorb the knowlege they chose to share with you.
    Jeff is worthy of respect and in no way is butt hurt like you seem to be, stop deflecting and put your focus into taking the advice and broaden your knowledge from more than just one source (Jeff)

    P.s. if you dont like the way i copied your “i think not” style of response, and classified it as trolling, then being a troll is by copying the troll – you called it, own it.

    • http://www.skooppa.com s.molinari

      The difference is, I didn’t reply to you in any personally way. I put up points, which you only acknowledged through your arrogant and negative reply, yes mocking my reply, instead of putting up proper arguments. You should look at your own replies lacking substance, which you actually admitted to. I’ll paraphrase. “I am only a commenter, so I don’t have to explain myself.”

      I wasn’t trolling, when I made my first comment. You brought this degrading conversation to me, and I took the bait. I am sorry I dropped to a troll’s level. You are right. I should have known better.

      It’s interesting how you think you know how Jeff feels and myself for that matter. “Butt hurt” is not how I feel. Slightly amused is more like it. And, at the same time, a bit disappointed in myself for taking the troll’s bait.

      Anyhow, I went and looked at who Douglas Crockford is. Yes, I didn’t know him. Go ahead and make fun of me for being so ignorant. It certainly won’t help the little motivation I already have through your vitriol.

      • http://www.nodewiz.biz/ Christopher Langton

        Feel free to act as though you have a moral high ground, fact of the matter is its taken you this long to look up Crockford which is all that was asked of you initially. Cudos, my work is done.

        Perhaps you should now do the same of other points made, actually take a second to read it. For example, Clearly you lost context of the “just a commenter” by omiting the point it made about the article claiming to prove the point i intend you to learn from Crockford but failing to do so.

        On the otherhand, i doubt you’ll act until you’ve argued several more points on how im trolling you but thats your loss, I only wished for you to expand your knowledge on the subject for which i should now apologise as its clear im an asshole.

        It’s not that I don’t think im a troll, clearly i must be if you keep calling me one, but i feel that trolling now has become quite helpful. I mean, i trolled when i asked you to read more on the topic , trolling when i pointed you toward Crockford, and you must have been a victim of a troll now you’ve been introduced to Crockford. Yes, trolls are the new constructive critic on the net.

        • http://www.skooppa.com s.molinari

          Please look at your first reply to me. It wasn’t a suggestion. It was an insult.

Recommended

Learn Coding Online
Learn Web Development

Start learning web development and design for free with SitePoint Premium!

Get the latest in JavaScript, once a week, for free.