0

MDN gives some examples as shown below,

// function declaration
function foo() {}

// function expression
(function bar() {})

// function expression
x = function hello() {}


if (x) {
   // function expression
   function world() {}
}


// function declaration
function a() {
   // function declaration
   function b() {}
   if (0) {
      // function expression
      function c() {}
   }
}

By definition, expression evaluates to a value.

From above example,

1) Is function world an expression or declaration? Because world looks like a declaration statement

2) Is function c an expression or declaration? Because c looks like a declaration statement

3) How do I understand the syntax for function bar in paranthesis? How var x = (function bar() {return 1}) is different from var x = function bar() {return 1}?

overexchange
  • 11,586
  • 11
  • 75
  • 203
  • they both have comments above them saying they are function expressions – dting Aug 22 '15 at 10:53
  • @DTing But am yet to agree with that. – overexchange Aug 22 '15 at 10:54
  • @overexchange A function declaration is very easily (and often unintentionally) turned into a function expression. A function declaration ceases to be one when it either: becomes part of an expression or is no longer a "source element" of a function or the script itself. A "source element" is a non-nested statement in the script or a function body: – artm Aug 22 '15 at 10:57

1 Answers1

3

The comments in the MDN page are very misleading. (MDN is a collaboratively-edited reference. It's normally excellent. Sometimes it falls a bit short of that.)

From above example,

1) Is function world an expression or declaration?

2) Is function c an expression or declaration?

Until ES2015 (aka "ES6"), they were both unspecified behavior because they were function declarations within a control-flow block. The specification didn't define how they should be handled, but handling them was an "allowable extension" under the spec (blech) and some engines did support them. Unfortunately, different engines supported them by doing different things.

As of ES2015, though, the spec takes them on: They're still function declarations, but how they're interpreted varies depending on whether...

  • ...the code is in strict mode (in which the behavior is rational and straightfoward),
  • ...the code is in loose mode on a browser-hosted JavaScript engine that implements the optional legacy behavior described by Annex B.3.3 and Annex B.3.4 of the spec (confusing, and there are only a couple of scenarios that are safe cross-browser),
  • ...or whether the code is in loose mode on a non-browser JavaScript engine, which in theory isn't supposed to implement Annex B (but that doesn't stop them).

Since in loose mode you can't be sure whether the JavaScript engine is going to implement the Annex B behavior or the non-Annex B behavior, the only reasonable choices are:

  • Use strict mode, or
  • Don't use block-level function declarations.

If you use strict mode (whether on a browser or not), the behavior is quite straightforward: The declaration is hoisted to the top of its block and is block-scoped (like let and const are). The identifier created by the declaration is writable, and so it's as though the declaration were converted to a function expression assigned to a let variable at the top of the block. Let's take the world example but add some to it.

This:

"use strict";
// ...
function example() {
    if (x) {
        console.log("testing:");
        console.log("1:", typeof world);
        function world() {}
        console.log("2:", typeof world);
    }
    console.log("3":, typeof world);
    console.log("4:", world === undefined);
}

...effectively becomes this:

"use strict";
// ...
function example() {
    if (x) {
        let world = function world() {};
        console.log("testing:");             // testing:
        console.log("1:", typeof world);     // 1: function
        console.log("2:", typeof world);     // 2: function
    }
    console.log("3:", typeof world);        // 3: undefined
    console.log("4:", world === undefined); // ReferenceError: `world` is not defined
}

Notice how the declaraton was hoisted to the top of the block, and is block-scoped.

Outside of strict mode, again, it depends, but the non-legacy version is just like strict mode but with var instead of let.

So: this:

This:

// NOT in strict mode
function example() {
    if (x) {
        console.log("testing:");
        console.log("1:", typeof world);
        function world() {}
        console.log("2:", typeof world);
    }
    console.log("3":, typeof world);
    console.log("4:", world === undefined);
}

...effectively becomes this:

// NOT in strict mode
function example() {
    var world;
    if (x) {
        world = function world() {};
        console.log("testing:");             // testing: (if executed)
        console.log("1:", typeof world);     // 1: function (if executed)
        console.log("2:", typeof world);     // 2: function (if executed) 
    }
    console.log("3:", typeof world);        // 3: function if `x` is truthy, undefined if `x` is falsy
    console.log("4:", world === undefined); // 4: false if `x` is truthy, true if `x` is falsy
}
T.J. Crowder
  • 879,024
  • 165
  • 1,615
  • 1,639
  • But language agnostically an expression evaluates to a value. So, assigning a function can be considered as expression. In python, `if 1: def f(): pass`, Is `f` an expression or declaration? – overexchange Aug 22 '15 at 11:02
  • @overexchange: I don't do Python, I'm afraid I don't know quite what you're asking in that question. – T.J. Crowder Aug 22 '15 at 11:08
  • So, `world` and `c` are in local context. Can you also answer my third question? – overexchange Aug 22 '15 at 11:14
  • @overexchange: The one you added after I answered? That's covered here: http://stackoverflow.com/a/13341710/157247 – T.J. Crowder Aug 22 '15 at 11:15
  • @overexchange: *"But language agnostically an expression evaluates to a value"* I can address that part: JavaScript has a statement called *ExpressionStatement*. It's for any (nearly) expression that appears where a statement is expected. It's why you can do `foo();` -- that's a function call expression, used as a statement. (Also why you can do horrible things like `value && foo();` without using `if`. But please don't. :-) ) – T.J. Crowder Aug 22 '15 at 11:17
  • Is `(function bar() {})` equivalent to `var bar = function bar() {}` and expression like `bar();` would actually execute that function? am still not clear. – overexchange Aug 22 '15 at 11:46
  • @overexchange: *"Is `(function bar() {})` equivalent to `var bar = function bar() {}`"* It would be, except that the first doesn't save the function reference anywhere and so it's a no-op. The second stores the function reference in the `bar` variable. (It's a dumb example on the MDN page, in my view, since it's pointless.) *"and expression like `bar();` would actually execute that function?"* Only in the second case; in the first case, we don't have any `bar` to call. – T.J. Crowder Aug 22 '15 at 12:02