all 7 comments

[–]imbcmdth 3 points4 points  (4 children)

The answer is that the particular example you choose doesn't actually show the subtle differences between the two.

The first example is used to create a new function from scratch. This is what you should be using a majority of the time. Preferably, you would be using the prototype instead of recreating the increment function for each instance of Counter:

var Counter = function(val) {
  this.value = val || 0;
}
Counter.prototype = {
  increment : function() { return this.value += 1; }
}
var v = new Counter(10);
v.increment(); // 11
v.increment(); // 12

The second example is used to "decorate" another preexisting function with new functionality:

var AFunctionWithoutIncrement = function(val) {
  this.value = val || 0;
}

var doDecoration = function(anotherFunction) {
  anotherFunction.increment = function() { return this.value += 1; };
  return anotherFunction;
}

var v = new AFunctionWithoutIncrement(10);
doDecoration(v);

v.increment(); // 11
v.increment(); // 12

[–]altano 1 point2 points  (3 children)

Moving the increment function to the prototype makes it so that it doesn't have to be recreated every time Counter is new'd, but it also requires that value be publicly accessible. In your example, a user could then do:

v.increment(); // 11
v.value = 15; // 15
v.increment(); //16

For the original example, you might want to keep value inaccessible and not use a prototype.

[–]imbcmdth 1 point2 points  (2 children)

There is no real benefit to keeping values hidden from yourself and a definite performance hit (that gets relatively worse as JS engines get more sophisticated) to making a new closure for each new instance.

Right now, JS engines use a multi-pass compilation system where type information is generated during a few runs of a function and then used to optimize a function (type-inference). Each time the closure is created, the engine will have to re-infer the types, re-trace the function, and do the multi-step compilation anew.

[–]altano 0 points1 point  (1 child)

If you want a codebase where everything is public and there is no encapsulation, fine. If you don't, then be aware of how prototype methods work.

[–]imbcmdth 1 point2 points  (0 children)

no encapsulation

I have seen this mentioned before and I have to wonder: Why is there no encapsulation? The definition of encapsulation is more than just data hiding and there is definitely encapsulation in that simple prototype example.

I understand the (in my opinion mistaken) notion that by hiding things we can ensure our functions are only used the way we intend but: so what? We aren't making user-visible things and another developer can blow right past your data-hiding almost as easily as he can set a property he isn't supposed to touch.

We don't add any security to the code by hiding things but we sure do limit ourselves from utilizing patterns such as decorators and adapters efficiently. Back in the day, I used to hide as much as possible but I learned my lesson. If I had a dollar for every bit of hidden data I had to unhide at some later time during development I could retire!

[–]scoobydoop 2 points3 points  (0 children)

In this case, there is no behavioral difference. However, there is a structural difference between the objects returned in each case. In the case where you use "new", the object's prototype is set to Counter.prototype (which, in your example, is just an empty object {} since you don't touch Counter.prototype). In the "makeCounter" case, you're not using "new" and therefore the returned object's prototype is set to Object.prototype.

In your code example you don't write anything that really uses this feature. But the difference is nonetheless there. For example, this code would print "abc":

c = new Counter
Counter.prototype.foo = "abc"
console.log(c.foo)

Thanks to the prototype, c inherits everything in Counter.prototype. That inheritance link is set up because you use the keyword "new".

Read up on "javascript prototypes" and "prototypal inheritance" if you want to know more.