As discussed here, function definitions can be used before they're defined. But as soon as a section of code is wrapped in a try block, this ceases to be the case.
This displays "Hello world":
hello();
function hello() { alert("Hello world"); }
But this displays "ReferenceError: hello is not defined":
try {
hello();
function hello() { alert("Hello world"); }
} catch (err) {
alert(err);
}
So there is clearly something "special" about a try block with respect to function declarations. Is there any way to get around this behavior?
Firefox interprets function statements differently and apparently they broke declaration hoisting for the function declaration. ( A good read about named functions / declaration vs expression )
Why does Firefox interpret statements differently is because of the following code:
if ( true ) {
function test(){alert("YAY");}
} else {
function test(){alert("FAIL");}
}
test(); // should alert FAIL
Due to declaration hoisting, function test
should always alert "fail", but not in Firefox. The above code actually alerts "YAY" in Firefox and I suspect the code that makes that happen finally broke declaration hoisting altogether.
I assume Firefox turns function declarations into var declarations when they are located in if/else or try/catch statements. Like so:
// firefox interpretted code
var test; // hoisted
if (true) {
test = function(){alert("yay")}
} else {
test = function(){alert("fail")}
}
After a brief debate with Šime Vidas, I have to say that Firefox's dealing with function declarations is non-standard, because of:
The production SourceElement :
Statement is processed for function
declarations by taking no action.
The production SourceElement : Statement
is evaluated as follows:
- Evaluate Statement.
- Return Result(1).
Both FunctionDeclaration and Statement are SourceElements, ergo, there should be no FunctionDeclarations inside a statement (if/else, try/catch). Give Šime Vidas a brownie!
Try/catch is basically another form of if/else and probably uses the same exception code.
Given that a function block establishes a local scope with forward function referencing, wrapping the contents of the try block in an immediate function seems to restore that behavior.
This works in Firefox, IE, Chrome:
try {
(function(){
hello();
function hello() { alert("Hello world"); }
}())
} catch (err) {
alert(err);
}
Of course functions and variables defined within the try-function are no longer visible in the catch block, as they would be without the immediate function wrapper. But this is a possible workaround for try/catch script wrapping.
You can always do it this way and get the best of both worlds:
function hello() {
alert("Hello world");
}
try {
hello();
}
catch (err) {
alert(err);
}
You will still get your exceptions in the catch block, but the function will be available. It should be easier to maintain as well, and there is no functional benefit to hoisting functions anyway.
Edit:
To demonstrate that this is just as durable as enveloping the entire code in a try catch, I'm providing a more detailed example.
function hello(str) {
alert("Hello, " + str);
}
function greet() {
asdf
}
try {
var user = "Bob";
hello(user);
greet();
asdf
}
catch (e) {
alert(e);
}
This will work as expected, no parsing issues. The only locations where it could fail at load time are outside of the function defs and the try catch. You will also get exceptions on any garbage inside of the function defs.
I guess it's a style preference, but it seems to be more readable and maintainable to me than other options.