Javascript workflow for dummies - scopes and variables

The thing with javascript development is that it is incredibly easy to begin with. This can really be the first language for anyone: No typing, extremely forgiving, extremely permissive. Basically you can write whatever you want and it will compile. Well, it won't work, but it will compile most of the time. And that's the problem, there's nobody to tell you that what you wrote is bad.

So when you write your first games (or whatever it is you create) this can be fine, you learn to play with it. But as time goes, you can easily see some problematic patterns that make you lose a lot of time every day, and are incredibly frustrating. Some examples:

  • Woops, I missplaced a , / . / : / = and my code means the opposite of what I thought
  • Oh, the variable i was actually already defined in the scope just outside of mine and it makes weird stuff
  • Oh shit, my script tries to access my other script but it isn't loaded yet
  • ...

So, I don't have answers for all of these problems, but I have some for many of them. This is why I am starting this series of "dummies" posts to show you some cool tools/patterns that will help you be more efficient with javascript. Not that you are a dummy, but it actually makes a cool title.

These posts will go in depth about the why and the how, so that you can really understand what you are doing.
This first post is about how scopes and variables work, which is important to understand right before starting to go wild.
Also, little disclaimer: These posts won't be exhaustive, and may not use proper vocabulary for everything. This is intended to be a functionnal introduction so you know what you're doing, not an official reference.

If you are new to programming, it may be a little hard to understand at first, so you may need some time to really get it right but you should because these are the most important things to know when programming.

How a program is read, scopes and variables

Before introducing the cool things I have planned for you, I have to explain a bit how scopes and variables work, and more generally how computer code is read. If you already know enough of this you can jump to the next post which introduces require.js.

So what is a scope? The short answer is that a scope is a block of code.

Basically, when your computer is processing javascript (and most of other programming languages) it has a "pointer" that is reading a chunk of code, and it can jump around the code (as an example when there is an if instruction).
So, what we call a scope is when the JS enters a new function. That is, each function produces a scope, and if you call a function from some place, you enter a child scope, while the place you were in is its parent scope. Basically the JavaScript machine enters a new scope when it calls a function, and goes back to its previous one at the end, doing all that until it is back at the top level and has finished program execution.

Let's see an example:

function init () {  
    returnTrue = true;
}
var useSecondFunction = true;  
function myFirstFunction () {  
    if (useSecondFunction) {
        return mySecondFunction();
    } else {
        return false;
    }
}

function mySecondFunction () {  
    if (returnTrue) {
        return true;
    } else {
        return false;
    }
}
init();  
myFirstFunction();  

So, here is what the code actually do here:

  • compile (We don't care about this for now)
  • runs the main scope - that is, the "root" of the script, which contains a variable declaration, two function definitions, and a call to myFirstFunction
  • The main scope encounters a function call
  • Script execution enters a child scope: The myFirstFunction scope
  • The computer runs the function myFirstFunction

Now here something interesting happens: The condition uses the useSecondFunction variable. But this variable isn't defined in our function, so what happens?

  • First, the program searches for the variable defined in the current scope (the mySecondFunction code). That is, a local variable, it only exists inside its scope. As you can see there isn't any useSecondFunction defined in the function so it doesn't find anything
  • Then, the program goes up in parent scopes and searches in each scope for local variables until it finds it
  • Back in the main scope, useSecondFunction is found. So the script uses this one. It then jumps to mySecondFunction, entering a new subscope.
  • Then we encounter another condition, this time looking for the returnTrue variable

So same thing as before, javascript goes up in scope until it finds it - but, will it find it?

Javascript is back to the main scope. It can't go anywhere else from now since it is the higher scope it can find. But there is no returnTrue variable to be found.

But you're wrong, the returnTrue variable is written in a function

Nope. As I told you, what is in a function is in a separate scope. What it means for our returnTrue variable is that it is in another scope (the scope of the init function), which we are not in. JavaScript doesn't search here.
So, JavaScript can't find our variable in its current scope. JavaScript is sad.

But there is hope for JavaScript, because it has one last thing to check: global variables.
Global variables are those variables who aren't in any scope. Or you could say, that are in a special master-scope accessible from anywhere. So when the code has finished searching every parent scope, what it will do is actually look in this special scope that contain global variables (they are accessible everywhere).

Ok right, how do I put a variable in the global bucket then?

See how there is no var when I defined returnTrue in the init function? That's the difference.

  • A variable defined with the var keyword first is local - it only exists in its scope and childs of its scope.
  • A variable defined without the var keyword is global. It is directly put in the global scope, and accessible anywhere as a "last resort" (ie. when the variable isn't found in any scopes)

So, back to our code. Our program hasn't found any returnTrue in any of its running scopes. But then it checks the global bucket, and happens to find that an anonymous scope defined a global variable returnTrue. So our variable does actually exist.
mySecondFunction can then return true since it knows that returnTrue is true.

Why should I care about scopes?

The reason why scopes are important is pretty simple: You will have problems with them unless you understand precisely their behaviour and know what you're doing. An example:

function getNumberOfPlayers () {  
    return numberOfPlayers;
}
numberOfPlayers = 10;

function setPlayerNumber (value) {  
    var numberOfPlayers = value;    
}

setPlayerNumber(5);

getNumberOfPlayers();  

So what will getNumberOfPlayers return? You may initially think 5, since we set the number of players to this value just one line before, but it will actually return 10.
As you can see in setPlayerNumber the variable numberOfplayers is declared with a var, meaning it is local to this function and only exists inside it.
So when well call getNumberOfPlayers just after that, it doesn't know about this variable. So it goes up and up until it finds the global version of numberOfPlayers which happens to be 10.

So this case isn't very complicated and you probably got it right in the first place, but this is just a few lines. When you start having complicated scripts with a lot of lines these sort of details can very easily start giving you problems.
And the most dangerous thing is that nothing will tell you you're wrong. Javascript won't start telling you "hey dude, I'm getting the global version of this variable because I didn't find anything, is it okay with you?" it will just use a variable that might not be the one you thought it is.

What you should do

So here is today's advice - which is not just an advice but the n°1 most important rule ever when writing javascript: DO NOT EVER DECLARE GLOBAL VARIABLES.

Always put a var in front of your variables declaration, so they exist precisely where they are meant to exist, and if you try to access them somewhere else it won't work, as it is meant to be.

It can be easier at first to just create global variables so you can access them anywhere without having to pass them around in function arguments or something, but it is wrong. Like, very wrong. Because eventually, when the project starts getting big you will spend hours debugging a wrong variable value that happens to be the fault of a global hanging in the wrong place.

Always use var when declaring your variables.

Now you can jump to the next part


comments powered by Disqus