ECMAScript’s FunctionDeclaration versus FunctionExpression

In ECMAScript, functions can be defined by a "FunctionDeclaration" or a "FunctionExpression". Basically, the difference is that a FunctionDeclaration includes a function name so that it can be called from other functions – it is defined as:

function Identifier ( FormalParameterListopt ){ FunctionBody }

while a FunctionExpression is the anonymous function – note in the definition that the "Identifier" part is marked as optional:

function Identifieropt ( FormalParameterListopt ){ FunctionBody }

However, including an identifier is still allowed. The identifier can be used inside the function to refer to itself, but not outside it.

This may confuse authors if we forget that whether something is a FunctionDeclaration or a FunctionExpression depends on the context. The right hand side of an assignment or the arguments of a function call will be a FunctionExpression, in other words behave like an anonymous function. For example

var a=function b(){return 'Hi'}; alert(b());

fails: there is no function named b there. alert(a()) would work. Likewise,

setTimeout( function a(){}, 50 ); a();

fails because the argument is evaluated as a FunctionExpression and after the setTimeout() call no function named "a" exists. (IE seems to violate/extend the standard here!)

It can be confusing that a FunctionExpression is allowed to look exactly like a FunctionDeclaration but behaves differently, and I don't really see a strong use case since arguments.callee exists.

5 thoughts on “ECMAScript’s FunctionDeclaration versus FunctionExpression

  1. To be continued: how they work (or don't) inside if(){} and with(){}. I'll probably post this on dev.opera.com when ready so criticism is welcome. 🙂

  2. @hallvors: Hmmm…. got me there.

    if((function b(){}) instanceof Function){alert(window.b)}
    // didn't expect this to be 'undefined' in non-IE

    Also didn't know IE would accept stuff like your examples. Good to know (could help prevent cluttering global object in IE). Any workarounds for IE, though? Is there no way to use it as in other browsers?

  3. JScript treats named functions as if they were function declaration, and initialises the identifier in the surrounding scope. The engines in other browsers are compliant and initialise the identifier in the contained scope. Which leads to horrible bugs in any code like this (paraphrasing some code I produced while I only had my mac available, so no iew testing):

    var
        foo={
            bar:function f(){
                /* bar code */
                setTimeout(f,200);
                },
            baz:function f(){
                /* baz code */
                setTimeout(f,200);
                }
            };

    Sure, arguments.callee works (that's the fix), but f is shorter.Inspired by bug 227785 I've been writing on an article about how function declarations and function expressions work, so I've got some code examples to show differences in behaviour. This snippet tests whether a named function is bound in the contained or the surrounding scope, to illustrate this behaviour of JScript:

    if(typeof b==='function')
        alert('b is compile time initialised in the surrounding scope');
    var
        a=function b(){return b},
        c=a(),
        b=null,
        d=a();
    alert(c===d?
        'Implementation is correct':
        'IMPLEMENTATION ERROR:nIdentifier bound in surrounding scope instead of local');

    Also a lot of code for other differences. This for example:

    bogus:{
        break bogus;
        function fn(){return'bogus!'}
    }
    alert(typeof fn==='function'&&fn());

    Is an example where JavaScriptCore compile time initialises fn but SpiderMonkey delays initialisation till control flow has reached it (which it of course won't).On the other hand, for an if statement:

    if(typeof fn==='function')
        alert('fn is compile time initialised');
    if(true){
        function fn(){return'true-clause'}
    }else{
        function fn(){return'false-clause';}
    }
    alert(fn());

    Here JavaScriptCore and SpiderMonkey both delay initialisation, while JScript and linear_b compile time initialise.

  4. liorean: feel free to beat me to that article for dev.opera :)Otherwise I hope you will allow me to use some of your examples if I get around to writing that article before you do?

  5. I'll see what I can do. I've had to put the article on ice for the moment since I've got two other articles I'm working on that are not hobby projects…And of course you can use my examples if you write the article before I do.

Leave a reply to liorean Cancel reply