JavaScript for C# programmers: Function basics and scope

Another post in the occasional series on programming in JavaScript from a C# developer's viewpoint.

This episode: the basics of functions in JavaScript, especially with regard to scope.

All right, here we are, finally. Writing some real executable code. Before we do, however, I have some very important information, and I need to make it a header, so you remember:

Functions are objects

This one concept, I believe, more than any other you've met so far, is the defining difference between C# and JavaScript. All functions are objects. You can assign them to variables; you can pass them around, for example as parameters to other functions; you can call them in various weird ways; you can give them properties; you can do those funky "functional programming" things to them like currying; and even the very definition of scope is function-based in JavaScript.

I'm sure you can see that there's a huge amount of information you're going to have to grasp here, but, if nothing else, remember this: functions are objects. Drill it into your subconscious. I know from experience it won't get there immediately, but I'll try and help it along. Functions are not bits of named code attached forevermore to a single class, like they are in C#, but are separate entities that you can manipulate in various, very interesting ways, as well as call them.

Defining a function

Back in the first installment of this series, I introduced a simple function to test whether a number was finite:

function isFiniteNumber(value) {
    return ((typeof value === "number") && isFinite(value));
}

And in the previous episode I showed a function to test whether a value was an array or not:

var isArray = function(value) {
    return (value) &&
        typeof value === "object" &&
        value.constructor === Array;
};

The two declarations look very different. The first one (known as a function statement) looks like a simple routine as you might have written it in C or Pascal, in the days before OOP. It seems to exist as an individual entity, separate from everything else; the kind of routine you can't write in C#, because every routine has to be a method on a class. The second one (known as a function expression) looks like a variable that holds a function (in other words, an object).

In fact, they are exactly the same, and work in the same way. The only difference is a rare problem dealing with where in your code you can declare function statements (the hoisting problem: different browsers can do it in different ways and you could get different results from the same piece of code).

The general recommendation is to use the var declaration version, for a couple of reasons. The first and the main one is that it explicitly states that the function is an object. You can't gain any other impression from the declaration: here's a variable called isArray and here's the function object we're setting it to. Once we've set isArray to this function object, we can call it using the () operator if we want. The second reason is that it forces you to think differently about JavaScript functions and to understand that they're not simple blocks of code attached to a single class/object for evermore. In particular, in JavaScript we can change the function that a variable points to very easily, and the function statement variant makes us think that the function is fixed; it's not.

Scope

This one is hard for C# programmers. Despite the fact that JavaScript looks so familiar with its C-style syntax, it uses a very different set of scoping rules than does C# or Java. Before we discuss the way JavaScript does it, let's revisit what scope means. Scope is the means by which programmers limit the visibility and the lifetime of their variables and parameters. Without scope, all variables would be global (and I've programmed in such languages and never want to again). In C#, scope is introduced by braces: you can declare a new variable inside some code in braces — a block — and that variable would not be visible outside the block, and in fact would be destroyed once execution reaches the closing brace. It's the most important invention to avoid global name collisions ever made.

In C# these braces can be the braces surrounding the code in a method, they can be the braces delineating a block for an if or a for statement, and so on. The essence is: declare a variable inside a block and it won't be visible outside. (If you want to read the full details about scope in C#, see section 3.7 of The C# Programming Language (Third Edition) by Hejlsberg et al. There are two pages of rules.)

In JavaScript, there is one basic rule: functions define scope. If you declare a variable in a function, it is visible anywhere inside that function, including inside nested functions, even before it was declared. It is not visible outside the function. Let's take a look:

var test = function(value) {
    var a = 1;
    var b = 2;
    var nested = function() {
        if (b === 2) {
            var a = "some string";
        }
        console.log("nested: " + a);
        console.log("nested: " + b);
    };

    nested();

    console.log("test: " + a);
    console.log("test: " + b);
};

test();

OK, some nonsense code, but let's dissect it. First of all we're declaring a variable called test and assigning it a function to it (from now on, I'll save on my typing and just say "declaring a function called test"). At the end we call this function variable. The test function first declares three variables, called a, b, and nested, then calls nested (it's a function) and logs the values of a and b.

The nested function tests to see if b is 2, and if so declares a string variable called a. It then logs the values of a and b.

Let's talk about the scope of the various variables. First of all, inside test, a, b, and nested are visible everywhere. Also the parameter called value is visible there too. In particular, all these variables are also visible inside nested (and, yes, nested is visible inside nested: we can do recursion). So far, nothing too surprising.

Let's look inside nested now. If you had your C# hat on, you'd say that the declaration of the variable a is only visible inside the if block, and that the first call to console.log() would fail to even compile. In JavaScript though, this new declaration of a is visible everywhere in the nested function; it is not limited to the if block. The variable b, since it's not redeclared inside nested, comes from the outer scope, that is, test. So the result of calling nested() is this:

nested: some string
nested: 2

Since a in the outer scope wasn't changed (the inner scope redeclared it), the logging for the outer scope produces this:

test: 1
test: 2

Having said all that, let's do some thought experiments. By all means use FireBug to verify what I describe is actually what's going to happen. First I'm going to initialize the value of b to 3, and rerun. What happens now? The if condition evaluates to false, and the declaration of the redefined a doesn't get executed. What does the function log then? The value of the outer scope's a? Or something else?

The answer is "something else". In fact, you'll get an error that a is undefined, not because it doesn't exist, but that it does exist but has no value. Remember: a variable declared anywhere in a function is visible everywhere in the function. Technically speaking, the declaration of a variable gets hoisted to the top of the function. If you like, the JavaScript interpreter rearranges the function to look like this:

    var nested = function() {
        var a;
        if (b === 2) {
            a = "some string";
        }
        console.log("nested: " + a);
        console.log("nested: " + b);
    };

and then executes it. As you can see, a is declared but not initialized if b were equal to 3. The first call to console.log() would fail.

Return to the original code. This time we'll "forget" the var keyword on the a in the nested function. What happens now? Yes, you've guessed it: both sets of logging produce the same answer with a being output as "some string".  The scope rules say that a in the inner scope uses the declaration in the outer scope.

A harder one, now. Rearrange the code to look like this:

var test = function(value) {
    var b = 2;
    var nested = function() {
        if (b === 2) {
            a = "some string";
        }
        console.log("nested: " + a);
        console.log("nested: " + b);
    };

    nested();

    console.log("test: " + a);
    console.log("test: " + b);
};

test();

Notice I've removed the declaration and initialization of a completely from the test function and inside nested a is not declared with a var keyword. What happens now? Does it all fail disastrously?

Answer, no it doesn't. Both logging calls for a display the same value: "some string". How come, since a doesn't exist?

The reason is that if you assign a value to a variable that hasn't been declared with a var keyword, JavaScript first of all works its way up the various nested scopes looking for a declaration to use (as we saw above). If it can't find one having reached the very outer scope, it obligingly assumes you wanted to declare the variable as a property on the global object and does so. You are, in effect, declaring a global variable, and you just know how nasty that can be. The global object for JavaScript running in a browser is called window, and the code above is interpreted to mean this:

var test = function(value) {
    var b = 2;
    var nested = function() {
        if (b === 2) {
            var window.a = "some string";
        }
        console.log("nested: " + window.a);
        console.log("nested: " + b);
    };

    nested();

    console.log("test: " + window.a);
    console.log("test: " + b);
};

In other words, this is not another scope rule at all, in fact, the very opposite. You are creating a global variable visible everywhere in your code. Also just imagine how difficult this would make the code to read and understand if the very last line of test was "var a;", for then a would no longer be a global variable at all.

(If you look back, the functions isFiniteNumber, isArray, and the various examples of test are all global variables defined on the window object. If you like, they all become methods on window. Needless to say this could be very bad. What if there already variables with these names in the window object? I've just clobbered them, that's what.)

Three Important rules

We're learned three important rules in this post, rules that don't apply in the C# world at all:

1. Always declare your local variables at the top of the function with the var keyword. You don't have to, but you will save yourself some scope problems later down the line. Understanding scoping in JavaScript can be difficult enough when coming from C#, so don't make it worse for yourself. If you like, put your Pascal hat on and declare what looks to be a var block.

2. Always declare your functions as variables using the function expression syntax. It'll help you remember that functions are objects.

3. Understand that, if you forget to declare a local variable, it will be created for you as a property on the global object, and will be visible globally. Global variables are bad in JavaScript, just as they are bad everywhere else. Don't create them either inadvertently or deliberately. Minimize the pollution on the global object.

Identification

The constructor function is Function. The typeof operator, applied to a function object, returns the string value "function".

By the way: we haven't finished talking about functions yet. Still to come, for example, is the value of this. As you may already have guessed, it's not quite the same thing as in C#... Until next time!

Album cover for The Best of Thelonious Monk Now playing:
Monk, Thelonious - In Walked Bud
(from The Best of Thelonious Monk)


Loading similar posts...   Loading links to posts on similar topics...

11 Responses

#1 Dew Drop - February 12, 2009 | Alvin Ashcraft's Morning Dew said...
12-Feb-09 7:43 AM

Pingback from Dew Drop - February 12, 2009 | Alvin Ashcraft's Morning Dew

#2 My Bad Attitude » JavaScript for C# programmers: Function basics and scope said...
12-Feb-09 1:22 PM

Pingback from My Bad Attitude » JavaScript for C# programmers: Function basics and scope

#3 JavaScript for C# programmers: Function basics and scope said...
13-Feb-09 10:08 AM

Thank you for submitting this cool story - Trackback from DotNetShoutout

#4 JavaScript for C# programmers: closure basics said...
21-Feb-09 5:47 PM

Continuing the series on JavaScript for C# developers, in this episode we look at closure. First the definition. A closure is a function that encloses its local environment when it is created. Now, in C#, the function talked about in the definition above

#5 JavaScript for C# programmers: prototypes, the basics said...
23-Feb-09 8:03 PM

Another post in the the series that discusses JavaScript for those more familiar with C#. In this episode, the first of a couple on the topic, we look at prototypes and prototypal inheritance. Despite the fact that the keyword class is reserved, there

 avatar
#6 Sai Gudigundla said...
24-Feb-09 5:36 PM

Thanks for the excellent explanation. You absolutely nailed some of the very difficult concepts of js namely Scoping and Functions.

Now I am never gonna forget that "FUNCTIONS ARE OBJECTS".

julian m bucknall avatar
#7 julian m bucknall said...
24-Feb-09 6:16 PM

Sai: I'm glad the post helped in its own small way to make you remember that functions are objects. And the rest, of course :)

Cheers, Julian

 avatar
#8 Mats said...
04-Mar-09 12:35 AM

Re. testing for Array, couldn't you just use instanceof Array?

julian m bucknall avatar
#9 julian m bucknall said...
04-Mar-09 9:45 AM

Mats: If you can guarantee that you would never be passing an array around different contexts in your application, then it would work (by context, I mean, a window or frame). Each context gets a different window global object, and when you write "foo instanceof Array" you are actually writing "foo instance of window.Array" for whichever window global object you have.

Creating an array in one context and then testing it with instanceof in another, means you are creating it with one global object, and testing it with another, and since they are different, instanceof will return false.

Now admittedly, the function I quote above will also fail in that scenario (the constructor test would fail), so it would have to be refined. Unfortunately JavaScript has nothing built in at this time to settle the matter once and for all.

(Hmm, I feel another blog post coming on...)

Cheers, Julian

 avatar
#10 Elijah said...
12-Dec-09 8:32 PM

I'm so glad I ran across your page. It has helped me get a slightly better understanding of the scope weirdness in Javascript.

Maybe you could write up something on the scope when dealing with functions that get registered with listeners as I tend to run into issues with functions declared in functions that need to get registered and called to do something for an object but always seem to lose the scope and then I just feel like I am hacking my way through it.

julian m bucknall avatar
#11 julian m bucknall said...
13-Dec-09 12:36 PM

Elijah: Good idea. Even after some time with JavaScript, I still get caught out, since it's my "second" language for development after C#, so another post exclusively on scope would help.

Stay tuned.

Cheers, Julian

Leave a response

Note: some MarkDown is allowed, but HTML is not. Expand to show what's available.

  •  Emphasize with italics: surround word with underscores _emphasis_
  •  Emphasize strongly: surround word with double-asterisks **strong**
  •  Link: surround text with square brackets, url with parentheses [text](url)
  •  Inline code: surround text with backticks `IEnumerable`
  •  Unordered list: start each line with an asterisk, space * an item
  •  Ordered list: start each line with a digit, period, space 1. an item
  •  Insert code block: start each line with four spaces
  •  Insert blockquote: start each line with right-angle-bracket, space > Now is the time...
Preview of response