JavaScript 101 – Side Effects & Hoisting

Actually, The title should have left JS 101 off, as programming with side effects is always a bad idea, but lets discuss it a bit. Javascript just happens to be in an environment that is exceptionally sensitive to side effects, whether in the form of style modifications, subtle differences between display settings or font changes. 

When building out the SDK I am maintaining, I have tripped over a number of completely fucked components of such nature that need to be ripped out, stuffed into a burlap sack, burned and then tossed off of a bridge into a torrential river – never to be seen again. Unfortunately we are not in a position to be able to conduct such an event without having some characteristically beautiful explosions, probably in someones pocketbook. We are hoping to address them in a rewrite – but that has not quite been scheduled. The truth is, planning such an undertaking is almost as overwhelming as the act of writing all of the test cases to cover the edge cases and make sure that some semblance of regression testing. Hopefully backwards compatibility wont be an issue… but we will see.

Lets look at a function and call:

var square = function (value)
{
    temp = value * value;
    return temp;
}

console.log(square(2)); // 4

At first glance, this looks perfectly reasonable, but it hides something insidious. By not simply returning the value and storing it in that temp variable we are actually doing something unintentional, and we may end up being shot for it. temp is actually a global in this context, since we haven’t declared it as a variable within our function.

var square = function (value)
{
    temp = value * value;
    return temp;
}

console.log(square(2), temp); // 4, 4

This example may appear contrived, but it is far from it. I have seen code like this from novices and experienced developers – it is trivial to overlook such a simple thing, but is can have drastic effects on the rest of your application. In fact, imagine if this was a key element that your application is built off of and it has been used through the codebase.

I don’t have to imagine it. =(

What should be going on is something more like the following:

var temp = 42;

var square = function (value)
{
    var temp = value * value;
    return temp;
}

console.log(square(2), temp); // 4, 42

Hoisting – A.K.A. Clobbersons Incorporated

Things become even more complicated within the world of JavaScript because we have features that are meant to be used to help keep us all honest – like Hoisting. I am currently using JS for a personal project, bound to UnrealEngine 4 and its been quite an interesting process that I will go into another time. I bring it up because game development is a field that requires some impressive context switching to be able to be productive. In one contexts, squaring a value may mean that you want to multiply a value with itself, in another it may mean that you are ensuring that two values are perpendicular, and in another yet it may mean that you intend to be modifying the an entities shape. Lets see what can happen if we aren’t careful.

Lets say we have to square the x component of a game object.

var getObjectSquare = function (obj)
{
    var value = square(obj.X); 
    var square = 42; // temp variable 

    [... MOAR? ...]

    return value;
};

console.log(getObjectSquare({X: 2}), temp); // FAILURE > 9000

What you should be asking yourself is… what the fucking hell is that? The truth is, if you have ever seen something like this you are likely unaware of Hoisting. If you have taken an old school C programming course, run by myself or any of my professors, there was a convention to always define variables at the tops of functions. This was done for convenience primarily, because you could always hide your variables in loops, tests and general code if you chose. JavaScript is willing to allow you to do the same, but will effectively punish you by silently making the value of that variable be undefined until it is formally set.

Variables are hoisted to the top of the current scope, which means that in the above example there is now an undefined square variable created before any of the code is executed. In effect, we have clobbered our project by defining a new variable that has the same name as a function we depend on. Note that this is only true for the sample there, and doesn’t leak out to the global namespace – within your function the global function wont exist.

Maybe thats what you wanted though =)

Thankfully in our sample these two lines of code are close in proximity. If you happened to have been working in an SDK that was on the order of 5000 lines long and need to figure out some dependency issues… that is an entirely different beast.

Closing thoughts

The truth is, all of the above were written to be unsecure and are nearly contrived for examples sake. You can avoid all of the above by properly ensuring that you lint your code, and don’t just muck around. Check out the code sample if you doubt my examples. =)

Code Sample | Hoisting

Leave a Reply