32

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?

Community
  • 1
  • 1
Chris Noe
  • 36,411
  • 22
  • 71
  • 92
  • Agreed; it doesn't work (Firefox), but the question I'd ask is why you need this? Why do you need to put function declarations inside a try/catch block? – Spudley Nov 01 '10 at 13:16
  • 1
    Firefox. I see now that IE and Chrome both display Hello world... – Chris Noe Nov 01 '10 at 13:16
  • I find that wrapping my entire script in a try/catch alerts me of otherwise silent failures. – Chris Noe Nov 01 '10 at 13:19
  • 1
    They are called "function declarations", not "function definitions". The official specification of JavaScript does not allow function declarations to appear inside statements. Why are you putting them inside? – Šime Vidas Nov 01 '10 at 14:36
  • Lots of good conversation. Now if you have existing code with use-before-defined references, what is the best way to trap script-load-time errors? – Chris Noe Nov 03 '10 at 01:04

3 Answers3

27

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:

  1. Evaluate Statement.
  2. 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.

Community
  • 1
  • 1
BGerrissen
  • 21,250
  • 3
  • 39
  • 40
  • @BGerrissen What do you mean by "either way is standard"? Function statements are not part of the official ECMAScript standard. They are something that Mozilla introduced. – Šime Vidas Nov 01 '10 at 14:58
  • @Sime http://bclary.com/2004/11/07/#a-13 (declaration) The afore mentioned option is in there somewhere as well, but too obscure to find atm. I've found it quite some time ago... – BGerrissen Nov 01 '10 at 15:09
  • Statement, declaration, pick one ;) – BGerrissen Nov 01 '10 at 15:09
  • ps. Function statements/declarations are indeed an extention of Mozilla, but it was also Mozilla that took JavaScript to ECMA, therefor function statements/declarations are standardized by ECMA. Coincidentally, Brendan Eich worked for Mozilla when he designed JavaScript. – BGerrissen Nov 01 '10 at 15:15
  • @BGerrissen I don't understand what you mean. I've read the spec and "functions statements" are not mentioned in it. – Šime Vidas Nov 01 '10 at 15:18
  • @BGerrissen It could be that Mozilla uses the term "function statement" to refer to function declarations. **However, the ECMAScript standard does not allow for function declarations to appear inside other statements (for example, inside an if statement).** – Šime Vidas Nov 01 '10 at 15:23
  • @Sime Vidas, can you link to that specific spec? Anyways, I updated my awnser since I cannot find the optional spec I was talking about, so perhaps my memory fails me. – BGerrissen Nov 01 '10 at 15:55
  • 2
    @BGerrissen You can use the same link that you provided in your comment above. First, scroll down to Chapter 14, where it states that a program is a list of statements and function declarations. Then, scroll up to Chapter 12. There, you can examine the `Statement` production. Here, you can see what statements there are and what their syntax is. You may have to browse trough the entire chapter (12), but you will see that no statement is allowed to contain the `FunctionDeclaration` production (which represents function declarations). – Šime Vidas Nov 01 '10 at 16:04
  • Chapter 12 is a wild goose chase, it just describes all statement types. Chapter 14 explicitly describes a SourceElement (in this case statement like if/else) is processed for FunctionDeclarations before being evaluated. This explicitly implies that FunctionDeclarations are allowed inside if/else and try/catch statements. (ps gave you +1 for making me look harder ;) – BGerrissen Nov 01 '10 at 17:00
  • @BGerrissen A SourceElement is processed for FunctionDeclarations because a SourceElement is either a FunctionDeclaration or a Statement. Chapter 12 defines all the Statement productions, ergo, the syntax of all the statements. Chapter 12 defines all the details about the constructs that can appear inside each type of statement, and function declarations are not defined inside any of the Statement productions. – Šime Vidas Nov 01 '10 at 17:09
  • @BGerrissen I have opened a question here http://stackoverflow.com/questions/4071292/may-function-declarations-appear-inside-statements-in-javascript My answer is NO, and I assume yours is YES. Let's see... – Šime Vidas Nov 01 '10 at 17:17
  • I stand corrected, updated my awnser. The ambiguity here is, that FunctionDeclarations are allowed inside statements, though how they are resolved differs from browser to browser as stated by Juriy "kangax" Zaytsev. – BGerrissen Nov 01 '10 at 17:25
  • @BGerrissen Yea, the ambiguity is present because there is a gap between the official standard and the actual browser implementations. But, that's a mess we have to live with :) – Šime Vidas Nov 01 '10 at 18:07
5

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.

Chris Noe
  • 36,411
  • 22
  • 71
  • 92
1

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.

sworoc
  • 1,461
  • 12
  • 13
  • This would not catch script-load-time errors. Unless you mean to move the entire script body into the hello() function, and then we have the same thing as the try-function wrapper approach. – Chris Noe Nov 01 '10 at 14:55
  • What do you mean by a load-time error? If you put any undefined reference inside the function definition, it will work as expected. It only checks the name of the function on initial load, and when called generates an exception handled by the try-catch. – sworoc Nov 01 '10 at 15:05
  • For example, accidentally type a garbage character on the line between the function and the try. – Chris Noe Nov 01 '10 at 15:36
  • If you put a garbage character before the try, they will all fail. If you just have function defs above, it works just fine. – sworoc Nov 01 '10 at 15:49
  • True enough. But when working in a large js file, the odds that I will typo somewhere in the middle of it are high. Hence the motivation for the try/catch wrapper. – Chris Noe Nov 01 '10 at 16:09