56

How are function declarations handled?

var abc = '';
if (1 === 0) {
  function a() {
    abc = 7;
  }
} else if ('a' === 'a') {
  function a() {
    abc = 19;
  }
} else if ('foo' === 'bar') {
  function a() {
    abc = 'foo';
  }
}
a();
document.write(abc); //writes "foo" even though 'foo' !== 'bar'

This example produces different outputs in Chrome and Firefox. Chrome outputs foo while FF outputs 19.

Not A Bot
  • 2,474
  • 2
  • 16
  • 33
HellaMad
  • 5,294
  • 6
  • 31
  • 53

4 Answers4

75

When this question was asked, ECMAScript 5 (ES5) was prevalent. In strict mode of ES5, function declarations cannot be nested inside of an if block as shown in the question. In non-strict mode, the results were unpredictable. Different browsers and engines implemented their own rules for how they would handle function declarations inside blocks.

As of 2018, many browsers support ECMAScript 2015 (ES2015) to the extent that function declarations are now allowed inside blocks. In an ES2015 environment, a function declaration inside of a block will be scoped inside that block. The code in the question will result in an undefined function error because the function a is only declared within the scope of if statements and therefore doesn't exist in the global scope.

If you need to conditionally define a function, then you should use function expressions.

Cheran Shunmugavel
  • 8,319
  • 1
  • 33
  • 40
  • 1
    Finally, someone got it right. Well done! Probably worth mentioning who implements what (i.e. almost everyone goes by the ES recommendation save SpiderMonkey). – davin Apr 09 '12 at 05:53
  • The statement that `the first example in the question is not legal syntax` is wrong, read davin's comment (which is spot on). Function statements are a legal extension to Mozilla's browsers. – RobG Apr 09 '12 at 09:18
  • @RobG: No, the answer is right, the first example is not legal as per the ECMAScript-262 Specification as only statements may appear in blocks and a function declaration is not a statement (extensions are not standard and not portable). – Jesse Good Apr 09 '12 at 20:35
  • 1
    @Jesse—the specification allows extensions, Mozilla have chosen to allow a **FunctionStatement** (which is not described by ECMA-262) within a block. Therefore it is not an error, but a deliberate extenstion allowed by the specification. It is certainly not a syntax error and no UA that I know of throws an error when encountering it, they just process it differently depending on whether they see it as a FunctionDeclaration or FunctionStatement. – RobG Apr 09 '12 at 23:01
  • @RobG, fair enough, I'll be more specific. – Cheran Shunmugavel Apr 10 '12 at 00:45
  • I find this subject a very confusing one. It seems like there isn't a concrete definition to what a function statement is. The answer stated here states that "FunctionDeclaration is not a Statement" yet Douglas Crockford states that "if the first token in a statement is function, then it is a function statement." I personally think we should do away with the terms function statements and function expressions and just call them functions. – Isaac Pak Feb 11 '17 at 21:17
  • strict mode sample is not working now. See [Conditionally created functions] :https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function – Tina Chen Apr 08 '18 at 09:01
  • 1
    @Tina, I need to update or delete this answer. ES2015 allows function declarations inside blocks. – Cheran Shunmugavel Apr 11 '18 at 06:16
7

From http://javascriptweblog.wordpress.com/2010/07/06/function-declarations-vs-function-expressions/

In javascript, you have function declaration:

function foo() {
}

and function expression

var foo = function() {
}

Quoting from http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting

“Function declarations and function variables are always moved (‘hoisted’) to the top of their JavaScript scope by the JavaScript interpreter”.

So what happened in your first example is that function declaration of function a(), gets hoisted to the top of the Javascript scope, thus producing 'foo' even though the if evaluates to false

Think of var foo as a normal Javascript statement, it's only executed on the runtime of your javascript, unlike function foo(), that's why the below is valid:

alert(foo());

function foo() {
   return 'gw ganteng';
}

Here, function foo() is parsed by the parser, putting foo() in the current scope, before attempting to call alert(foo())

http://javascriptweblog.wordpress.com/2010/07/06/function-declarations-vs-function-expressions/

In JavaScript execution there is Context (which ECMA 5 breaks into LexicalEnvironment, VariableEnvironment and ThisBinding) and Process (a set of statements to be invoked in sequence). Declarations contribute to the VariableEnvironment when the execution scope is entered. They are distinct from Statements (such as return) and are not subject to their rules of process.

Andreas Wong
  • 59,630
  • 19
  • 106
  • 123
2

The ECMA-262 v5 requires implementations to register all function and variable declarations during the first pass when entering any new global or function-level execution context. Chrome is technically doing it right here because it is looking inside the else and then blocks and registering a() prior to execution. Unfortunately it produces the most unreadable results.

FF is waiting until it evaluates the if statement before it evaluates and adds function and variable declarations to the current context. BTW. Both browsers do it this way inside catch and finally clauses.

It really is just a matter of two different ECMA implementations dealing with a feature that shouldn't be there to begin with. The scenario at hand shows why function declarations shouldn't be inside control flow statements.

drankin2112
  • 4,715
  • 1
  • 15
  • 20
0

Function declarations are not accessible outside {} blocks.

if (true) {
  function sayHi() {
    alert("hii");
  }
  sayHi(); //accessible
}

sayHi(); //error, not accessible since out of the block

If you want to define conditional functions, use function expressions like

let sayHi;
if (true) {
  sayHi = function(){
    alert("hii");
  }
  sayHi(); //accessible
}

sayHi(); //accessible
Nitin Jadhav
  • 6,495
  • 1
  • 46
  • 48