Jump to content

Closure vs Prototype


inductible
 Share

Recommended Posts

One of the hardest things I've found in getting to grips with JavaScript is translating a classical, statically typed mindset into the dynamical, prototypal world.

 

I'm currently trying to find a nice, regular pattern to use as my bread-and-butter structure for writing and instantiating objects; I believe that the ability to make private properties and methods is genuinely useful, and so am particularly enjoying the 'module' pattern using closures. I've read a little about the negative performance implications of closures vs prototypes, and have checked on jsperf: http://jsperf.com/prototype-vs-closures/20 - where it appears that closures are generally slower to  init, but quicker to read/write; the lesson I'm learning: prototypal architecture suits objects that are read/written very little but instantiated lots (particles?), and closures work for 'chunkier' implementations that benefit from a proper 'public' interface, with other properties hidden (game controller?).

 

Do you guys have a preference or any wisdom either way? 

 

I'm trying to master JS before delving into TypeScript, which I know will make this a moot point!

 

 

Link to comment
Share on other sites

I generally always use prototypal objects, unless the module pattern is required for some reason. If you *need* private variables `Object.defineProperty()` can do that for you.

 

The big thing I want to mention is, you don't have to mimic classical inheritance. Look at twitter's presentation of functional mixins: https://speakerdeck.com/anguscroll/how-we-learned-to-stop-worrying-and-love-javascript

Link to comment
Share on other sites

Having looked into aspect orientated programming I'm a little dubious about its suitability for game development - mixins sound pretty cool though (a bit like a component based approach). The terrifying thing about Javascript is that there are so many ways of structuring the very fundamentals of a system, each method has its evangelists, and it's too easy to become confused as the the 'right' way to do things; there clearly isn't a well defined right way! Not that there ever was in Flash/AS3 - but then, the 'ways that worked' were pretty obvious and easy to lock onto - this is certainly not so with Javascript.

 

I'm beginning to acknowledge that the language is powerful enough to assume numerous architectural 'forms'; some of these are better suited to some system 'behaviours' than others - a setup using 'mixins' may be good for games (like Unity's component model), but an aspect oriented one may benefit an enterprise banking system or 'traditional' web application front end.

 

The prototypal approach seems the most common amongst game dev examples, perhaps because it represents Javascript's intended 'class-like' usage (and is reminiscent of AS2 for veteran devs). Module pattern feels so much more elegant though!

Link to comment
Share on other sites

Explanation of the jsperf results:

 

Prototype init is faster than closure init because objects created from the prototype don't need to allocate memory for the functions. They are part of the prototype, and all instances share the prototype functions (instance.get_name is just a reference to Person.prootype.get_name). A feature of this approach is that if you edit the prototype, you automatically update the implementation of all instances. Using closures is the opposite - every instance creates its own copy of the functions. You could edit the implementation of an instance, and you will only change that instance, the other instances will remain the same. There is no way to update all the instances after they have been created without updating each one of them one at a time.

 

Prototype function calling is slower than closures - this is a side-effect of what I just described above. In a prototyped object, if you call person.get_name, the javascript vm needs to do the following:

 

Check if person.get_name is defined (in this case it is not, get_name was defined in the prototype, not the object)

Check if person.prototype.get_name is defined (in this case it is)

(If it was not found, it will look for person.prototype.prototype.get_name, and repeat until the end of the prototype chain)

 

So you see, when you call a prototype function, it needs to spend a bit of time 'looking' for the function. The longer your prototype chain is, the longer it takes.

 

Since closures have their own copy of the functions, when you call person.get_name, the function is immediately available to it. It doesn't need to look up the prototype chain to 'find' the function.

 

You can get around the slower call time for prototype functions by keeping a local reference to the function, with something like var func = Person.prototype.get_name, and calling func directly.

Link to comment
Share on other sites

That's all true, however in real life it's rarely so simple, as there are many more factors to consider. Take for example the following code:

for (i=0; i<large_number; i++){    person[i].get_name();}

In this case calling a prototype function is likely to be faster than closures (especially if get_name is computationally intensive), because you're calling the same function over and over, as opposed to different functions, and it's likely to be cached for quicker memory access.

 

So in general I don't think you can say that one approach is always faster than the other.

 

While it's good to know the theory (you never know when you may need it), in real life, if you're making a game that you want to work on mobiles, chances are that your bottlenecks will be elsewhere (fill rate for example).

 

In which case I wouldn't worry too much about the performance implications of using prototypes vs closure, just go with what's more convenient for your coding style. Then profile and if (and only if) using prototypes or closures appears to be a major problem, spend time worrying about it. Also different browsers do things differently, and while optimizing for one browser you could be un-optimizing for another.

Link to comment
Share on other sites

Your time will be taken up with logic and rendering mostly. If you want a bit of OO and some separation I recommend a combination of AMD with requireJS for modules sprinkled with John Resig's simple inheritance for classes and extensibility: http://ejohn.org/blog/simple-javascript-inheritance/

 

I'm currently writing a JS raycaster you could look at for an example of some of these things. It's a little messy cause I'm refactoring the casting from the game module into a cast one: https://github.com/digitalicarus/caster

 

Another game (lode runner clone) I started and didn't finish used resig's method and modules: https://github.com/wee-enterprises/motherlode. You don't even have to use just one of these techniques. You can mix and match. I use basic JS OO for things that won't need to be extended (engine stuff ususally) and resig's small OO code for things like entities.

 

Don't concern yourself too much with these minor nuances. JSperf is not super accurate. A difference of even up to 8+% can just be circumstantial noise (run LOTs of samples). Write a lot of code that does things you actually want to do and profile it. It's great to be performance minded, but real testing on real game code trumps small contrived testing. For instance, I increased the performance of my caster recently simply by pre generating all my trig tables. This was by far even more performant than the switch to typed arrays (which I also did). JS objects are quite fast.

 

That being said, JSPerf is a lot of fun and good to litmus test when you need quick info. Just beware the contrived nature of the test and the fact that many other factors in the browser affect JS performance so you'll get a lot of noise in your data.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...