Grokking JavaScript Variables

Until I switched to my current job, I’d always been more of back-end kind of developer. Throw Hibernate mapping files at me, ask me to come up with and implement an algorithm in C/Java/C++/C#, or create webpages with Struts + JSTL, I’d be fine. But my knowing of CSS and JavaScript for heavy client-side work was strictly in the sense that I knew *of* them.

Which was a problem, because our screens were very complex and dynamic–they contained stuff that you’d usually implement using Swing or SWT, instead of CSS+HTML+JavaScript. The UI elements in our screens were really “alive”, each of them backed by a significant amount of JavaScript code behind them. Validation logic was complex because we needed to inform the users early if they did something wrong, instead of telling them after they’ve submitted their input to the server.

I quickly realized one thing: JavaScript is very easy to pick up. It’s easy to code simple stuff, and be done with it. However, coming up with well-designed, high-performance, easily-maintainable JavaScript code is another matter altogether. You really need to understand the language and how it differs from Java. So I’ll put what I’ve learned about JavaScript here, particularly how it’s surprised me as a Java guy.

The Confusing Var

Unlike in Java, JavaScript variables can be either global or local (local scope is also known as “function scope”). It is still not a good idea to pollute the global namespace with variables or functions, but JavaScript allows you to do it if you want to. But whether a variable is global or local doesn’t only depend on where it is declared, but also how you declare it. Consider this:

i = 1; // declared outside any function, global

var j = 2; // yes, global too

function foo() {

k = 3; // inside foo(), should be local?

var l = 4; // idem

} // foo()’s scope ends here

foo();

alert(“i = “ + i); // expected — global

alert(“j = “ + j); // expected — global

alert(“k = “ + k); // k is global?!!

alert(“l = “ + l); // expected — undefined outside foo()

So to recap:

  1. Variables that are declared globally always become global.
  2. Variables that are declared inside a function’s scope become local to that function, if they are declared with the “var” keyword. In other words, if you declare a variable inside a function without the “var” keyword, you’re effectively creating (or trampling the value of) a global variable.

To make my life easier, I *always* prefix all variable declarations with “var. I suggest you do the same. It’ll make your code much more consistent and easy to track. Consistency is not the only reason why the var prefix is always a good idea. If you’re declaring a variable inside a function without var, then JavaScript has to search all the scopes to find out whether the variable already exists. Other than being an unnecessary performance drag, it’s also risky because you may end up trampling a global variable without you knowing. There’s a good article that discusses this in IEBlog, you should take a look if you haven’t already.

Also note that at no time do we ever specify the type of variables. You can do this:

var i = “blah”;

i = 5;

i = false;

i = 3.14159;

Which is obviously illegal in Java.

Another twist on function scope

Unlike Java, JavaScript variables can really only have either global scope or function scope. There’s no other kind of scope. Inside a function, you can declare a variable 10 level deep within nested fors and ifs and whathaveyou, and the scope is still function scope. For example:

function bar() {

// m is already in scope here, although it still has no value

alert(“In bar, m = ” + m);

if(true) {

// j will also be in scope throughout bar()

for(var j = 0; j < 5; j++) {

if(true) {

var m = 5;

}

}

}

// now m’s value is 5

alert(“In bar, m = ” + m);

}

bar();

// this will raise an error–m is not defined

alert(“Outside bar, m = ” + m);

Note that the first alert() will print “In bar, m = undefined”. m is already in scope, because it is declared inside bar(), albeit deep inside nested ifs and fors. The last alert(), however, causes an error “m is not defined”, although probably it’s more accurate to say m is not declared (outside bar(), that is).

So that’s another confusing twist about JavaScript variables: there are two kinds of undefined. One is where a variable is actually already declared but not assigned any value yet, so you can print it, but the value will be “undefined”. The other is when a variable is not declared at all (so try printing it or accessing it will raise an error).

My takeaway from this is that instead of scoping variables as narrow as possible like in Java, inside a JavaScript function, it actually makes more sense to declare all variables at the top of the function.

Points To Remember

  1. JavaScript variables can only have either global scope or function scope. If a variable is declared inside a function (with “var”), it is local to the whole function, from the beginning to the end of that function, regardless of where it is declared.
  2. Always prefix all variable declarations with the “var” keyword.
  3. Declare all local variables at the beginning of the enclosing function. I feel that this is a matter of style, though. Especially for loop counter variables, I tend to define them within the for() anyway.
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s