The code in question is simple:
console.log("So it begins.");
foo();
function foo() { console.log("In foo()."); }
console.log("So it ends.");
Why does foo()
execute, before it is defined (retrospective edit: in Chrome and Safari)?
I tinkered with this a bit, testing the following code in Chrome, Safari and Firefox:
javascript:foo();function foo() { alert("Oh."); }
An alert is displayed in Chrome and Safari, while Firefox remains silent.
Is there any explanation for this surprising, inconsistent behavior?
A function expression is very similar to and has almost the same syntax as a function declaration (see function statement for details). The main difference between a function expression and a function declaration is the function name, which can be omitted in function expressions to create anonymous functions.
The function declaration (function statement) defines a function with the specified parameters. You can also define functions using the Function constructor and a function expression.
Statements are essentially always an “imperative” task that must be carried out. On the other hand, functions are a collection of multiple code lines which you are able to call all at once. Functions themselves are not statements, because they are not an imperative task that must be performed.
Hoisting. With JavaScript functions, it is possible to call functions before actually writing the code for the function statement and they give a defined output. This property is called hoisting. Hoisting is the ability of a function to be invoked at the top of the script before it is declared.
Javascript delarations always will be moved to the top. It's called hoisting:
Example that should work in all browsers: http://jsbin.com/abelus/edit
While others explained the "hoisting" behavior of functions (I personally find calling it context preparation or pre-processing more clear than hoisting) the reason of the different behavior of Firefox remains unanswered.
To start with, you should know the difference between a Function Declaration and a Function Statement.
A Function Declaration, as in your example can happen only in two places, in global code (outside of any function) and directly within the Function Body of another function, for example:
function foo () {}
function bar () {
function baz() {}
}
All the above functions are valid Function Declarations.
The ECMAScript Specification doesn't allow to define Function Declarations in other places for example within Blocks:
if (true) {
function foo () {}
}
The above function should give you a SyntaxError
exception, but most implementations are benevolent, and they will still pre-process (hoist) the function, even if the actual function is not reachable (e.g. if (false) { function bar() {} }
).
In Firefox, Function Statements are allowed, meaning that the function definition actually happens when the control reaches that specific statement, for example:
if (true) {
function foo () { return true; }
} else {
function foo () { return false; }
}
Executing foo();
after the above statements, in Firefox will produce true
, because the first branch of the if
statement is actually executed.
In other browsers, foo();
produces false
because all the functions are pre-processed when entering the execution context, and the last one will take precedence, even if the false
branch of the if
statement is never reached.
The Firebug console, executes its code wrapping it inside a try-catch
block, that's why the function is not available before its declaration.
If you try on the console:
console.log(typeof f); // "undefined"
function f () {}
You will see that f
isn't prepared yet, but if you wrap your code inside a function, you will see the expected behavior:
(function () {
console.log(typeof f); // "function"
function f () {}
})();
Again, that's because now f
is defined as a Function Declaration, since it exist in the body of the anonymous function, and is not part of an statement Block.
The behavior you are noticing is called function hoisting.
In a nutshell, functions that are defined using the syntax function foo() { ... }
are "hoisted" to the top of the scope in which they are defined, thus allowing them to be called before being defined.
With that being said, this is an obscure part of JavaScript. It would not surprise me if there are differences in how browsers implement it (particularly if you are using an older version of firefox).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With