This is neither a scope problem nor is it a closure problem. The problem is in understanding between declarations and expressions.
JavaScript code, since even Netscape's first version of JavaScript and Microsoft's 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}
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:
- Anything to the right of an
=
sign (or :
on object literals).
- Anything in parentheses
()
.
- 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) before the invention of the let
keyword.