15
  var foo = function(){ return 1; };
  if (true) {
    function foo(){ return 2; }
  }
  foo(); // 1 in Chrome // 2 in FF
  //I just want to be sure, is FF 4 not "standard" in this case?

Edit:

what if we have this:

  var foo = function(){ return 1; };
  if (true) function foo(){ return 2; }      
  foo(); // is 1 standard or is 2 standard?
Paul Biggar
  • 27,579
  • 21
  • 99
  • 152
Pacerier
  • 86,231
  • 106
  • 366
  • 634
  • 3
    Is there a reason you would expect this code to have predictable results? – Jamie Treworgy Apr 22 '11 at 17:18
  • Why not do the following? http://jsfiddle.net/cmcnf/ – Prisoner Apr 22 '11 at 17:19
  • @jamietre from what i remember function declarations should be hoisted right at top.. so the function that return 1 will override it. of course im not 100% sure myself – Pacerier Apr 22 '11 at 17:19
  • 1
    @Prisoner The code in the jsfiddle you posted does not match the code in Pracerier's original post. Here is a proper one: http://jsfiddle.net/cmcnf/1/ I'm running Chrome, and the jsfiddle I just created returns 1 and the one that Prisoner created returns 2. – donut Apr 22 '11 at 17:25
  • It looks like chrome parses any inline function definitions before the rest of the code. If you do this code in realtime in the debugger in chrome, it returns 2. If you do it in a script (e.g. in jsfiddle) it returns 1. – Jamie Treworgy Apr 22 '11 at 17:29
  • 2
    @AndrewMoore function declarations are hoisted to the nearest function scope not block scope. 1 is correct. – Raynos Apr 22 '11 at 17:32
  • @Heandel http://qwepo.com/test.html donut beat me to it.. – Pacerier Apr 22 '11 at 17:35
  • 1
    @jamietre Chromes console is a bad place to test stuff, I hit a lot of strange evaluation bugs with it in the past :/ – Ivo Wetzel Apr 22 '11 at 17:50
  • 4
    I think the consensus is just don't do this! – Davy8 Apr 22 '11 at 17:54

5 Answers5

31

The original poster's code isn't permitted by the ECMAScript standard. (ECMAScript the official name for the JavaScript language specification, for legal reasons.) It is, however, a common extension to the language—one which is, unfortunately, implemented differently in different browsers.

In standard JavaScript, function definitions may only occur in top level code, or at the top level of a function's body. You can't have conditionals, loops, or even curly braces between the enclosing function's body and the the function definition.

For example, this is permitted:

  function f() {
    function g() {
      ...
    }
  }

but this is not:

  function f() {
    {
      function g() {
        ...
      }
    }
  }

What complicates the picture is that most browsers do accept this latter code, but each assigns its own idiosyncratic interpretation to it. Firefox treats it like:

  function f() {
    {
      var g = function g() {
        ...
      }
    }
  }

The ECMAScript committee is considering choosing a specific interpretation for these "function statements" (as opposed to function definitions). They haven't made a decision yet. Mozilla is discussing its preferred solution here.

Jim Blandy
  • 1,536
  • 10
  • 17
16

The code in the question is not actually allowed at all by current ECMAScript syntax (as of ECMAScript 5). You can do var foo = function() {} inside a block, but you can only do function foo() {} at the toplevel in functions or scripts.

Currently browsers support the code in the question in incompatible ways because they're all implementing extensions to the core language and they implement different extensions. A fully conforming ECMAScript 5 implementation would actually end up with a SyntaxError when compiling this script.

There are proposals to add the ability to do this sort of thing to ECMAScript, but they're not quite finalized yet.

Tim Down
  • 318,141
  • 75
  • 454
  • 536
Boris Zbarsky
  • 34,758
  • 5
  • 52
  • 55
  • I replaced the pipes with backticks to enable code formatting for your code examples. Were the pipes significant? – Tim Down Apr 25 '11 at 22:58
  • Nope. Just a convention from other places I've typed code; I hadn't discovered backticks yet when I wrote that answer. Thanks for fixing things! – Boris Zbarsky Apr 26 '11 at 00:31
12

Both are technically wrong, according to the ECMAScript standard, because function declarations are only allowed at the top level or directly inside other functions. Technically a function declaration inside an if block is a syntax error. Most implementations allow it as in extension, but they interpret it differently.

The reason for the difference is that Chrome is treating the foo "declaration" as a normal function declaration and hoisting it to the beginning of the scope. Firefox (for historical reasons IIRC) only declares the function when the if statement gets executed.

To better demonstrate the difference, you could try running this similar code:

console.log(foo()); // 2 in Chrome, error in FF

var foo = function(){ return 1; };
console.log(foo()); // 1 in both Chrome and FF

if (true) {
    function foo(){ return 2; }
}
console.log(foo()); // 1 in Chrome // 2 in FF

Edit: Your second example is exactly the same. JavaScript doesn't have block scope, only function-level and program-level. The "problem" isn't that the function declaration is in a block, it's that it's not a top-level statement.

Matthew Crumley
  • 101,441
  • 24
  • 103
  • 129
  • 4
    +1. I think the historical reason for Firefox's behavior is so you could write `if (ie3) { function f() {...} } else { function f() {...} }` and the if-statement would determine which function you got. But yeah, it was never a sane language feature. – Jason Orendorff Apr 22 '11 at 18:08
11

There is no specified behavior for a function declaration not found at the top level of a program or at the top level of a function body. Or, rather, the specified behavior is a syntax error, because the JavaScript grammar doesn't allow such function declarations. The reason for the different behaviors is that browsers historically have been all over the map here, and they remain so due to existing sites written with browser-specific code paths that make it impossible for anyone to change.

Strict mode prohibits this syntax, for what it's worth, and it's likely a future version of ECMAScript will define it. But for now you should not use it, because its behavior is not precisely defined by specs, and you'll get different behavior in different browsers.

Jeff Walden
  • 7,008
  • 2
  • 38
  • 55
1

fiddle. This is Undefined Behaviour. It's a mess.

How firefox interprets it is handled by the other answers

How chrome interprets it

var foo = function() { return 1 };
if (true) {
    function foo() {
        return 2;
    }
}
console.log(foo());

What's acctually happening is function foo is being declared then overwritten with a local variable foo immediately.

This gets translated into

function foo() {
    return 2;
}
var foo;
foo = function() { return 1 };
if (true) { }
console.log(foo());
Raynos
  • 166,823
  • 56
  • 351
  • 396
  • What's interesting is that Firefox isn't getting the hoisting wrong, it's *scoping* wrong. It fails to hoist the function because it's inside braces. If you remove the `if (true) { }` then FF returns 1. I was going to say it was unlikely this bug would cause any problems, but this raises other concerns about how FF scopes. – Jamie Treworgy Apr 22 '11 at 17:40
  • @jamietre yes that is correct. Function declarations might be block based. I checked. It's only function declarations are parsed by block. The function is bound to function scope it's just not declared until the block is parsed. – Raynos Apr 22 '11 at 17:43
  • 4
    In any event, anyone who puts function declarations inside conditional blocks deserves what they get. – Jamie Treworgy Apr 22 '11 at 17:50
  • 1
    Firefox is perfectly correct here. The program is not permitted standard ECMAScript code; rather, it uses syntax that many browsers have implemented as an extension --- each with different semantics. "Firefox... [is] scoping wrong"? Give us a little credit. – Jim Blandy Apr 22 '11 at 18:08
  • @JimBlandy I've fixed the answer. I wouldn't go as far as to say that firefox is correct. Supporting function declarations inside blocks is undefined behaviour. but you are correct I attacked firefox unneccessary because I didn't understand that function declarations in blocks were unallowed. – Raynos Apr 22 '11 at 18:11