Explain the encapsulated anonymous function syntax

Viewed 79226

Summary

Can you explain the reasoning behind the syntax for encapsulated anonymous functions in JavaScript? Why does this work: (function(){})(); but this doesn't: function(){}();?


What I know

In JavaScript, one creates a named function like this:

function twoPlusTwo(){
    alert(2 + 2);
}
twoPlusTwo();

You can also create an anonymous function and assign it to a variable:

var twoPlusTwo = function(){
    alert(2 + 2);
};
twoPlusTwo();

You can encapsulate a block of code by creating an anonymous function, then wrapping it in brackets and executing it immediately:

(function(){
    alert(2 + 2);
})();

This is useful when creating modularised scripts, to avoid cluttering up the current scope, or global scope, with potentially conflicting variables - as in the case of Greasemonkey scripts, jQuery plugins, etc.

Now, I understand why this works. The brackets enclose the contents and expose only the outcome (I'm sure there's a better way to describe that), such as with (2 + 2) === 4.


What I don't understand

But I don't understand why this does not work equally as well:

function(){
    alert(2 + 2);
}();

Can you explain that to me?

10 Answers

Great answers have already being posted. But I want to note that function declarations return an empty completion record:

14.1.20 - Runtime Semantics: Evaluation

FunctionDeclaration : function BindingIdentifier ( FormalParameters ) { FunctionBody }

  1. Return NormalCompletion(empty).

This fact is not easy to observe, because most ways of attempting to get the returned value will convert the function declaration to a function expression. However, eval shows it:

var r = eval("function f(){}");
console.log(r); // undefined

Calling an empty completion record makes no sense. That's why function f(){}() can't work. In fact the JS engine does not even attempt to call it, the parentheses are considered part of another statement.

But if you wrap the function in parentheses, it becomes a function expression:

var r = eval("(function f(){})");
console.log(r); // function f(){}

Function expressions return a function object. And therefore you can call it: (function f(){})().

Those extra parenthesis creates extra anonymous functions between global namespace and anonymous function that contains the code. And in Javascript functions declared inside other functions can only access namespace of parent function that contains them. As there is extra object (anonymious function) between global scope and actual code scoping is not retained.

You can also use it like:

! function() { console.log('yeah') }()

or

!! function() { console.log('yeah') }()

! - negation op converts the fn definition to fn expression, therefore, you can invoke it immediately with (). Same as using 0,fn def or void fn def

Related