38

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.

tounano
  • 859
  • 2
  • 9
  • 17
  • You might wanna have a look at this http://www.phpied.com/3-ways-to-define-a-javascript-class/ – Sachin Jain Dec 16 '13 at 08:39
  • Thanks for comment. I'm familiar with those ways... However I've seen a great debate of what's the best practice. I'm looking to find a way and stick to it through out my code. – tounano Dec 16 '13 at 08:42
  • Then choose the one which makes the most sense to you. Option 2 and Option 3 are basically identical in the outcome btw. – Felix Kling Dec 16 '13 at 08:52

3 Answers3

48

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).

EDIT in 2023

It's rained a lot since I wrote this answer and it is no longer relevant. Just use class and worry about real problems.

bgusach
  • 14,527
  • 14
  • 51
  • 68
7

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();
ovnia
  • 2,412
  • 4
  • 33
  • 54
  • 6
    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`. – Felix Kling Dec 16 '13 at 08:49
  • 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 Dec 16 '13 at 08:53
  • 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); }` – ovnia Dec 16 '13 at 08:56
  • 7
    *"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. – Felix Kling Dec 16 '13 at 09:07
  • 1
    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? – xec Dec 17 '13 at 14:23
4

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'
SJ00
  • 638
  • 1
  • 6
  • 10