May 12, 2014 · technical projects javascript

Never forget that Javascript hates you

My background is OO programming. First with C++ in the early 90s, then the shiny Design Patterns book in '95, then years upon years of Java.

Strolling briskly into the world of contemporary Javascript with an OO mindset will lead you to a dark place of heartbreak and disappointment. As I noted in an earlier post, trying to do what I would call normal OO in Javascript is, like drunk blindfolded surgery, both messy and unsafe.

It's not just that there's a single potential error, as I've often read. The whole approach is tortured, error-prone and requires a slew of protective guard functions to stop you going off the rails. You can watch Douglas Cockroft talking about his issues with the JS language design and proposing workarounds.

The title of this post is stolen from this article bashing JS as a functional language. I can't say I'm enough of a functional programmer yet to comment on its fairness but the soundbite is catchy.

There's a certain school of thought - evidenced here on stackoverflow - that you should just back away from the new keyword and do things in a functional style. For this project that's essentially what I've done. For the cases where I need to generate many instances of Foo, I define a factory function Foo.newInstance and encapsulate what I need there. I don't use inheritance.

My view might change over time as I get more comfortable with the patterns, but for the moment it's more productive to simply not think of JS as an OO language. Working in a purer functional style is actually very refreshing. I found that not spending hours agonising and revisiting complex super/sub relationships results in clearer code.

Let's clean up this town

Now that I'd decided conclusively that the THREE style of OO was a terrible and confusing style to emulate, I began ripping out all the faux-OO code.

I think there's only one remaining use of this in the codebase, which is well understood - attaching an update() function to a new Shot object.

This was a good time to clean up some oddities dating from the early days when the overall structure wasn't clear yet. So the core actors[] array now belongs to State for example, rather than awkwardly sitting in the main encounter.js.

There is no spoon()

During my research I also discovered there is a fun difference between function declarations and function expressions. That should be pretty simple to understand right? Regard the length and detail in that answer and form a view.

Just another bit of nuanced JS magic I need to understand and refactor at another time. The immediate cost of my ignorance is that I'm missing a bunch of semicolons at the end of function expressions, as shown below.

// a function *declaration*. No trailing semicolon
function log(msg)
{
  console.log(Math.floor(clock.oldTime) + ' ' + msg);
}

...versus

// a function *expression*
UTIL.platformSupportsTouch = function()
{
  return 'ontouchstart' in window;
} // technically a needs semicolon here

It is not surprising to me that there are so many average Javascript programmers out there. For a beginner-friendly language, the number of pitfalls and foggy areas is nightmarish.

Given that, I think my self-imposed emphasis on code clarity is paying off. I committed many aggressive changesets that worked first time.

At the end of the overhaul, I was delighted to take out my comment reading, // FIXME what a piece of shit object model

100% statistics

I spent some time ditching the basic STATS widget and wired in the quite lovely rstats alternative. This gives you a compact graph of performance over time and some basic threshold support to make it go red when your FPS dips.

The THREE plugin seems quite useful, counting geometries, faces and the like. When I get to FPS optimisation on mobile devices it will pay off. For the moment though I need the screen space back, so it's turned off. Not before raising an issue about the weird hard-coded alert once your scene breaks 1,000 WebGL faces.

Next up: creating touch support for mobile devices like my Nexus 5.

Comments powered by Disqus