There are a few conclusions here about the difference in performance:
The second form (using the
prototype
field on constructors) is much more memory efficient and faster at the instantiation time because all values are already defined ahead of the time. A new object instantiation does not incur the instantiation of new properties besides those created inside the constructor itself. Whereas on the former case you need to instantiate all of the new function objects alongside with storing their Environment Record and optimising access for closured variables along the way, for every new object.Still expanding on the first point, this actually happens because JS uses delegative inheritance, rather than concatenative. Therefore even the least optimised object created through the second form, initialising it would be just a matter of making a pointer to a Hashtable that contains the methods. This gives you blazing fast instantiations, but slow as fuck property accesses.
To optimise property access, objects that are created through the same path (same [[Prototype]] field, same properties set in the same order, etc.) get the same hidden class, which optimises static property accesses to plain memory offsets. So you also get really fast property access with delegative inheritance, instead of having to manually find the value in each hashtable of each prototype in the chain.
new Foo()
is more of an idiom thanfoo()
for creating new instances of objects, therefore JITs optimise heavily for this. I am not familiar with the internals of any optimising JIT, but I wouldn't be surprised if they compiled stuff optimistically beforehand to optimise the object creation time. One of the reasons I believe this is so is that dynamic inheritance with prototypes, by way of Object.create(foo) is not as heavily optimised, nor is simulatingnew
. This is likely to be becausenew
is an operator, therefore optimistic optimisations are easier to implement —new
can't meandelete
,Object.create
can!
Some of these are just hypothesis, since as I said, I'm not familiar with any of the internal implementations of any of the JITs. I wrote a performance test comparing the different ways of instantiating an object before for performance here, which is a bit more thorough: http://jsperf.com/constructing-objects
@kevincennis no, callbacks are not the "whole point of asynchronous programming", they're just an implementation detail — and a terrible one at that! They are used in JavaScript for a couple of reasons:
1) It's extremely trivial to do manual continuation-passing style in a language with first-class functions. You just replace return foo
with f(foo)
.
2) The language doesn't come up bundled with anything else out of the box, so people just go with the approach that has least initial friction to get things working.
There are lots of problems with this approach, however:
1) Callbacks DO NOT compose. They create tightly coupled, call-site specific code, which in turn leads to maintainance hell — this is the actual problem, "callback hell" is, in fact, a myth. You can think about callbacks as a fancy word for "goto-based programming". Yeah, that doesn't sound good, does it?
2) In the long run, manual-continuation passing style will lead to lots of duplicated code, and code that are can't be easily abstracted over, this is a side-effect of Problem 1.
3) Callbacks do not integrate with all the other constructs in the language. They feel like little alien things, where you're always forced to know whether you're dealing with eventual values or immediate values. IOW, you code as close to the bare metal as you could (just like "goto").
There are other, better approaches for asynchronous programs, that allow you to actually compose things, in turn decoupling your code and making it more maintainable. Promises/A+ implementations are one way to do that. Other languages have things like Monads, Delimited Continuations and Promises/Futures.
I wrote a follow-up for this comment here: https://coderwall.com/p/ehzcuq
@planet I fail to see why you'd abuse prototypes like that. The whole thing about prototypical OO is that everything is an object, and objects inherit directly from other objects. Thus,
{ a: b }
is always a singleton, no need to go to lengths to construct something that looks like a class, then instantiate that class — in fact, JS only has instances, no classes :P