5

What exactly is different about these 2 objects, a created using a constructor, b using a closure?

Is the property __proto__ useful for anything that cannot be achieved by using a closure? Am I supposed to use these techniques in different situations? Is there a difference in memory usage?

(jsFiddle)

window.MODULE = {};

MODULE.constructor = function(){
        this.publicVariable = 10;
};
MODULE.constructor.prototype.publicMethod = function(){
    return this.publicVariable;
};
//-------------------------------//
MODULE.closure = (function(){
    var publicMethod = function(){
        return this.publicVariable;
    };
    return function(){
        var obj = {};
        obj.publicVariable = 10;
        obj.publicMethod = publicMethod;
        return obj;
    };
}());
//-------------------------------//
var a = new MODULE.constructor();
console.log("constructor", a.publicMethod(), a)
var b = MODULE.closure();
console.log("closure", b.publicMethod(), b)

Also see a more complicated jsFiddle comparison with some private and static properties - both techniques work the same as far as I can tell...

Aprillion
  • 21,510
  • 5
  • 55
  • 89
  • note there is only 1 instance of `publicMethod` in both cases, so prototype version should not consume less memory than closure in this particular case – Aprillion Oct 07 '13 at 12:50
  • 1
    You still need extra fields on the object tables to point to the shared public method and you won't be able to share any private methods you create. That said, for the vast majority of Javascript programs the the coding style differences will be more important than performance. Finally, you are still free to mix both styles if you really want to but private methods must be implemented with closures and you wont be able to monkey match them. – hugomg Oct 07 '13 at 12:59
  • @missingno you can share private methods using prototypical inheritance? – Aprillion Oct 07 '13 at 13:07
  • you can't share private methods because private methods are closures and each closure needs to close over a different set of variables. The methods probably share their inner "code pointer" but since JS functions are mutables the function objects themselves cannot be shared (and as far as I know, most implementations dont optimize this). That said, the performance shouldnt matter unless you are doing something really performance sensitive (in which case you are going to need to worry about how different implementations optimize your code) – hugomg Oct 07 '13 at 13:42
  • 1
    @deathApril You can have "private" (called privileged) methods by having a function return a prototype that contains closures. – HMR Oct 07 '13 at 15:45

3 Answers3

6

Roughly, the main advantages of prototypal inheritance (your "constructor" method) are makes it easier to do subclasses and monkeypatching (adding methods to an existing class) while closures are nice if you want private instance variables and methods ("protected methods" are still hard to do with either method) or prefer lexical scoping instead of dynamic scoping with "this" (useful if someone does setTimeout(obj.method, 1000))

As for performance, prototypal inheritance can be a bit more efficient because implementations are tuned to it but it really shouldn't matter unless you instantiate lots of objects from that class.


Private variable example:

var counter  = function(){
   var count = 0; //private variable

   var increment = function(){ // private method
      count ++;
   };

   return { //public interface
      next: function(){
         increment();
         return count;
      },
   }
}

var c = counter();
console.log(c.next());
console.log(c.next());
Pavlo
  • 43,301
  • 14
  • 77
  • 113
hugomg
  • 68,213
  • 24
  • 160
  • 246
  • I'm a bit confused about "private instance variables and methods". Could you add examples please? – Pavlo Oct 07 '13 at 13:20
  • Got it. And what about "protected methods"? Does it mean "non-rewritable"? – Pavlo Oct 07 '13 at 13:42
  • 1
    @Pavlo: There are ways to get protected methods but they are really complicated and not worth it, IMO. I prefer to just use public variables with the underscore naming convention if I need "protected" stuff. – hugomg Oct 07 '13 at 13:45
  • Thanks. Btw, looks like you got syntax problem on line 1. You probably wanted to use function declaration or expression. – Pavlo Oct 07 '13 at 13:52
1

If we skip the advantages of OOP approach, difference between a and b is that b has own method publicMethod, when a inherits this method from it's prototype:

a.hasOwnProperty('publicMethod'); // false
b.hasOwnProperty('publicMethod'); // true

Note, that __proto__ property is non-standard and not supported in IE < 11. Alternatively you may use ES5 feature Object.getPrototypeOf(a).

Also note naming conventions it JavaScript1:

  • UPPER_CASE is used for constants, when globals usually use PascalCase, so your MODULE should be Module;
  • constructors use PascaleCase too, so your MODULE.constructor should be Module.Constructor.
Pavlo
  • 43,301
  • 14
  • 77
  • 113
  • any reference for the naming convention? – Aprillion Oct 07 '13 at 13:30
  • @deathApril I'm trying to find a good one, and it's surprisingly hard. Google Search pops up Crokford's naming conventions, but amongst the hundreds of projects I never saw them in use. I will be adding them, when I will find good ones. – Pavlo Oct 07 '13 at 13:38
  • 1
    @deathApril Also many projects use this naming conventions de facto, i. e. I'm working with Ember right now, and Ember does it: http://emberjs.com/guides/concepts/naming-conventions/ – Pavlo Oct 07 '13 at 14:01
  • and other projects use e.g. `jQuery` or `$` for names of globals... but ProperCase for Globals and Constructors sounds reasonable :) – Aprillion Oct 07 '13 at 18:18
  • That difference easily disappears if you move publicMethod to "this.publicMethod" in the constructor instead of MODULE.constructor.prototype.publicMethod. Then there is no difference other than the __proto__ property? – Niko Bellic Aug 24 '14 at 20:40
0

More on prototype, inheritance, overriding and calling super here: https://stackoverflow.com/a/16063711/1641941

MODULE={};
MODULE.constructor = function(){
  this.publicVariable = 10;
};
MODULE.constructor.prototype=(function(){
  var privateMethod=function(){
   console.log("I'm private");
  };
  return {
    publicMethod : function(){
      //going to call "private" method
      privateMethod();
      return this.publicVariable;
    }
  };
}());
var c = new MODULE.constructor();
c.publicMethod();

To have instance specific "private" member values you have to give up prototype because you have to define all functions accessing these values in the constructor function body instead of on the prototype. I personally think it's silly to forcefully implement something that is not supported by JavaScript (private instance specific variables) and by doing so giving up something that is supported by JavaScript (prototype).

By supported I mean that JS engines will optimise your code for it.

Community
  • 1
  • 1
HMR
  • 37,593
  • 24
  • 91
  • 160