views:

106

answers:

3

Why does the first one of these examples not work, but all the other ones do?

// 1 - does not work
(function() {
setTimeout(someFunction1, 10);
var someFunction1 = function() { alert('here1'); };
})();

// 2
(function() {
setTimeout(someFunction2, 10);
function someFunction2() { alert('here2'); }
})();

// 3
(function() {
setTimeout(function() { someFunction3(); }, 10);
var someFunction3 = function() { alert('here3'); };
})();

// 4
(function() {
setTimeout(function() { someFunction4(); }, 10);
function someFunction4() { alert('here4'); }
})();
+1  A: 

Because someFunction1 has not yet been assigned at the time the call to setTimeout() is executed.

someFunction3 may look like a similar case, but since you are passing a function wrapping someFunction3() to setTimeout() in this case, the call to someFunction3() is not evaluated until later.

matt b
But `someFunction2` has not yet been assigned when the call to `setTimeout()` is executed either...?
jnylen
@jnylen: Declaring a function with the `function` keyword is not precisely equivalent to assigning an anonymous function to a variable. Functions declared as `function foo()` are "hoisted" to the beginning of the current scope, while variable assignments occur at the point where they are written.
Chuck
+1 for functions being special. However just because it _can_ work doesn't mean it should be done. Always declare before you use.
mway
@mway: in my case I've organized my code within a "class" into sections: private variables, event handlers, private functions, then public functions. I need one of my event handlers to call one of my private functions. For me, keeping the code organized in this way wins out over ordering the declarations lexically.
jnylen
+1  A: 

Javascript's scope is function based, not strictly lexical scoping. that means that

  • Somefunction1 is defined from the start of the enclosing function, but it's content is undefined until assigned.

  • in the second example, the assignment is part of the declaration, so it 'moves' to the top.

  • in the third example, the variable exist when the anonymous inner closure is defined, but it's not used until 10 seconds later, by then the value has been assigned.

  • fourth example has both of the second and third reasons to work

Javier
In your first bullet point, you mean `someFunction1`?
jnylen
tks, fixed.....
Javier
+12  A: 

This is neither a scope nor is it a closure problem. The problem is in understanding between declarations and expressions.

Javascript code, since even Netscapes first version of javascript and Microsofts first copy of it, is processed in two phases:

Phase 1: compilation - in this phase the code is compiled into a syntax tree (and bytecode or binary depending on the engine).

Phase 2: execution - the parsed code is then interpreted.

The syntax for function declaration is:

function name (arguments) {code}

name and arguments are of course optional (code is optional as well but what's the point of that?).

But javascript also allows you to create functions using expressions. The syntax for function expressions are similar to function declarations except that they are written in expression context. And expressions are:

  1. Anything to the right of an = sign.
  2. Anything in braces ().
  3. Parameters to functions (this is actually already covered by 2).

Expressions unlike declarations are processed in the execution phase rather than the compilation phase. And because of this the order of expressions matter.

So, to clarify:


// 1
(function() {
setTimeout(someFunction, 10);
var someFunction = function() { alert('here1'); };
})();

Phase 1: compilation. The compiler sees that the variable someFunction is defined so it creates it. By default all variables created have the value of undefined. Note that the compiler cannot assign values yet at this point because the values may need the interpreter to execute some code to return a value to assign. And at this stage we are not yet executing code.

Phase 2: execution. The interpreter sees you want to pass the variable someFunction to setTimeout. And so it does. Unfortunately the current value of someFunction is undefined.


// 2
(function() {
setTimeout(someFunction, 10);
function someFunction() { alert('here2'); }
})();

Phase 1: compilation. The compiler sees you are declaring a function with the name someFunction and so it creates it.

Phase 2: The interpreter sees you want to pass someFunction to the setTimeout. And so it does. The current value of someFunction is its compiled function declaration.


// 3
(function() {
setTimeout(function() { someFunction(); }, 10);
var someFunction = function() { alert('here3'); };
})();

Phase 1: compilation. The compiler sees you have declared a variable somefunction and creates it. As before, its value is undefined.

Phase 2: execution. The interpreter passes an anonymous function to setTimeout to be executed later. In this function it sees you're using the variable someFunction so it creates a closure to the variable. At this point the value of someFunction is still undefined. Then it sees you assigning a function to someFunction. At this point the value of someFunction is no longer undefined. 1/100th of a second later the setTimeout triggers and the someFunction is called. Since its value is no longer undefined it works.


Case 4 is really another version of case 2 with a bit of case 3 thrown in. At the point someFunction is passed to setTimeout it already exists due to it being declared.


Additional clarification:

You may wonder why setTimeout(someFunction, 10) doesn't create a closure between the local copy of someFunction and the one passed to setTimeout. The answer to that is that function arguments in javascript are always, always passed by value if they are numbers or strings or by reference for everything else. So setTimeout does not actually get the variable someFunction passed to it (which would have meant a closure being created) but rather only gets the object that someFunction refers to (which in this case is a function). This is the most widely used mechanism in javascript for breaking closures (for example in loops).

slebetman
That was a seriously great answer.
Matt Briggs
This is probably a lack of understanding of closures, but I always thought of it as access to an scope, not as creating something between one scope and another. I also thought it was at the scope level, not the variable level. Would you mind either elaborating a bit more on that, or pointing me in the direction of something I can read up on? Again, great answer, wish I could vote it up twice.
Matt Briggs
This answer makes me wish I could vote up multiple times on the same answer.Truly a great answer. Thanks
ArtBIT
@Matt: I've explained this elsewhere (several times) on SO. Some of my favourite explanation: http://stackoverflow.com/questions/3572480/please-explain-the-use-of-javascript-closures-in-loops/3572616#3572616
slebetman
@Matt: Also: http://stackoverflow.com/questions/61088/hidden-features-of-javascript/2047391#2047391
slebetman
@Matt: Technically, closures involve not scope but stack frame (otherwise known as activation record). A closure is a variable shared between stack frames. A stack frame is to scope what an object is to class. In other words, a scope is what the programmer perceives in the code structure. A stack frame is what is created at runtime in memory. It's not really like that but close enough. When thinking about runtime behavior, a scope-based understanding is sometimes not enough.
slebetman
@slebetman: That totally makes sense, thank you :) Building on your analogy, a closure would be a private static variable (in java terminology), right? I guess this is what I get by learning about them from languages like javascript and ruby rather then a real functional language.
Matt Briggs
Thanks. Edited the question title and tags to reflect the fact that this isn't really about closures.
jnylen