What's the recommended way of creating objects in NodeJS?

81,675

Solution 1

The most efficient way is the following:

  • Put all the essential initialisation in the constructor (e.g. validate constructor parameters, set properties, etc).

  • Set methods in the .prototype property of the constructor. Why? Because this prevents from re-writing each method each time you create an object. This way you recycle the same prototype for each object you create. Efficient in memory and sparing coding time.

  • Do not use closures for private properties. Why? It is slow, and prevents you from using this object in an inheritance chain (the pseudo-private vars don't belong to the object, they're just accessible). It is instead common practice to use an underscore when declaring an object property, to indicate it is a _private property that should not be accessed from outside.

  • Use new instead of Object.create. It's easier to remember if you are used to other OOP languages; and at the end new uses Object.create under the hood.

In other words, something like this:

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

Person.prototype.sayHello = function () {
    alert('My name is: ' + this._name);
};

var john = new Person('John');
john.sayHello();

EDIT

Some extra information:

  • Object.create vs new. Benchmark here. Although the question is for Node.js, I think the same behaviour is to be expected. (any correction is welcome)

  • Closures to emulate private properties: You can read about in this question.. The point that the private/closure properties do not belong to the object is a programming fact: they are accessible by the object methods but do not belong to the object. When using inheritance, that is a big mess. Besides, only methods that are declared in the constructor have access to the closure. Methods defined in the prototype do not.

  • Defining methods in the constructor or the prototype property: read this question, and take a look of this benchmark

EDIT 15/04/2016

The points I made here three years ago are still right from a performance point of view, but my opinion about what is the "recommended way" has changed a little in the meanwhile. Factory functions are in general a good option, which would be the OP's first approach. Just an example:

function Person(name) {
    return {
        sayHello: function () { alert('My name is: ' + name); }
    };
}

and then just do:

var p = Person('John');

In this case you trade flexibility (no new coupling, ease of composition with other "mix-ins") and simplicity (no this mess, easy object instantiation) for some speed and memory. In general they are perfectly valid. If you have performance issues, and those are because of this way of creating objects, revert to another method. The Object.create approach is also good, falling somehow in the middle of new and factory functions (side note: the new class syntax is syntactic sugar for new+ prototype)

Summing up: my recommended way is starting from the simplest and easiest way of creating objects (factory functions) and then fall to other methods when you hit performance issues (which in most of cases is never).

Solution 2

There are many ways to create "Class" and "Object" in JS. I prefer this way:

var MyObject =
        function(args) {
            // Private
            var help = "/php/index.php?method=getHelp";
            var schedule = "/php/index.php?method=getSchedules";
            var ajax = function(url, callback, type) {
                //....
            }

            // Public
            this.property = 0;
            this.getInfo = function() {
                // ... 
            }

            // Constructor
            function(data) {
               this.property = data;
           }(args);
        };

var o = new MyObject();

Solution 3

Note: If you are more familiar with OOP syntax, you can also use class which is just syntactical sugar over existing prototype-based way.

Performance Comparison between 4 ways of creating object - with constructor (Chrome 61 - https://jsperf.com/create-object-ways)

Option A: Using return (Fastest 3x)

Option B: Using {key:value} (1.5x)

Option C: Using prototype (1x) <- Base

Option D: Using class (1.02x)

Option A seams to perform best. Note that some of the performance boost is because it avoids use of new or object.create. So just to have a fair trial, here is another test between method-only objects without constructor and properties.


Performance Comparison between 4 ways of creating methods-only object (Chrome 61 - https://jsperf.com/create-static-object-ways)

Option A: Using return (3.2x)

Option B: Using {key:value} (Fastest 3.3x)

Option C: Using prototype (1.8x)

Option D: Using class (1.9x)

Option B outperformed Option A a little. Also the bottleneck caused by object.create was more then new.


Best Practice

Option A (using return) is best performing in both the scenarios. This way can get little messy if you have many methods and properties.

I prefer to divide constructor & properties in separate object using Option A and methods in another object using Option B. This approach does need to send an extra instance reference in parameters but can be useful if you have multiple objects using same properties & constructor (Some OOP inheritance can also be achieved).

Example:

// Constructor & Properties Object (Using option A)
var UserData = function(request){

  // Constructor
  if ( request.name )
    var name = request.name;
  else
    var name = 'Not Available';

  if ( request.age )
    var age = request.age;
  else
    var age = null;


  // Return properties
  return {
    userName: name,
    userAge: age
  };

};


// Object methods (Using Option B)
var Adults = {

  printName: function(instance){ // Read propery example
    console.log( 'Mr. ' + instance.userName );
  },

  changeName: function(instance, newName){ // Write property example
    instance.userName = newName;
  },

  foo: function(){
    console.log( 'foo' );
  }

};


// Object methods (Using Option B)
var Children = {

  printName: function(instance){
    console.log( 'Master ' + instance.userName );
  },

  bar: function(){
    console.log( 'bar' );
  }

}


// Initialize
var userData = UserData ( {name: 'Doe', age: 40} );

// Call methods
Adults.printName(userData); // Output 'Mr. Doe'
Children.printName(userData); // Output 'Master Doe'

Adults.foo(); // Output 'foo'
Children.bar(); // Output 'bar'

Adults.changeName(userData, 'John');
Adults.printName(userData); // Output 'Mr. John'
Share:
81,675
tounano
Author by

tounano

Updated on October 01, 2020

Comments

  • tounano
    tounano over 3 years

    I'm building a composite for request module, however I'm not sure what's the best practice regarding building objects in JS for Node.

    Option 1:

    function RequestComposite(request) {
      return {
        get: function (url) { return request.get(url); }
      }
    }
    var comp = RequestComposite(request);
    
    • Note: I know that i should call a CB in async way, but for the ease of explaination I return it...

    Option 2:

    function RequestComposite(request) {
      this.request = request;
    }
    
    RequestComposite.prototype.get = function (url) { return this.request.get(url); };
    var comp = new RequestComposite(request);
    

    Option 3:

    var RequestComposite = {
      init: function (request) { this.request = request; },
      get: function (url) { return request.get(url); }
    }
    var comp = Object.create(RequestComposite).init(request);
    

    I tried to find my way around, however I got even more confused about how should I use objects...

    Would the answer be different if I want to use objects for browsers?

    Thanks.

  • Felix Kling
    Felix Kling over 10 years
    Why do you prefer that way? What are the advantages and disadvantages? How does it compare to the ways mentioned in the question? And more specifically: What's the point of the "private constructor"? As far as I can see, you are just immediately executing a function and assign undefined to __construct.
  • ovnia
    ovnia over 10 years
    I can't highlight any specific pros and cons, it's just another way. I prefer this way, coz it is the closest to classsic OOP way to create class and it looks more native to me heh)
  • ovnia
    ovnia over 10 years
    As you know, there are no constructors in js, so i use such function just to make some initializations in class. But, you can make something like this var MyObject = function(args) { var __construct = function(data) {}(args); }
  • Felix Kling
    Felix Kling over 10 years
    "As you know, there are no constructors in js" There are no classes in JS, but every function can be a constructor function. It just seems unnecessary to have var __construct = function(o) { o.property++; }(this);, because it is equivalent to this.property++;, which looks less complicated.
  • bgusach
    bgusach over 10 years
    Hi, I added some extra info.
  • xec
    xec over 10 years
    After the last edit, the comment // Constructor doesn't make much sense, as MyObject already is the constructor function. The code accompanying it doesn't make sense to me either, care to explain what it's supposed to do?
  • SJ00
    SJ00 over 6 years
    How is .prototype more efficient in performance? In fact, it performed the worst in benchmark test - jsperf.com/create-object-ways
  • klewis
    klewis about 6 years
    Can you show reference to your claim that Object.Create uses new under the hood?