ChatGPT解决这个技术问题 Extra ChatGPT

Explain the encapsulated anonymous function syntax

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?

I think all these varied notation and ways of defining/setting/calling functions is the most confusing part of initially working with javascript. People tend not to talk about them either. It's not an emphasized point in guides or blogs. It blows my mind because it's something confusing for most people, and people fluent in js must have gone through it too. It's like this empty taboo reality that never gets talked about.
Also read about the purpose of this construct, or check a (technical) explanation (also here). For the placement of the parenthesis, see this question about their location.
OT: For ones who want to know where these anonymous functions are used a lot, please read adequatelygood.com/JavaScript-Module-Pattern-In-Depth.html
This is a typical case of Immediately Invoked Function Expressions (IIFE).

c
coolsaint

It doesn't work because it is being parsed as a FunctionDeclaration, and the name identifier of function declarations is mandatory.

When you surround it with parentheses it is evaluated as a FunctionExpression, and function expressions can be named or not.

The grammar of a FunctionDeclaration looks like this:

function Identifier ( FormalParameterListopt ) { FunctionBody }

And FunctionExpressions:

function Identifieropt ( FormalParameterListopt ) { FunctionBody }

As you can see the Identifier (Identifieropt) token in FunctionExpression is optional, therefore we can have a function expression without a name defined:

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

Or named function expression:

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

The Parentheses (formally called the Grouping Operator) can surround only expressions, and a function expression is evaluated.

The two grammar productions can be ambiguous, and they can look exactly the same, for example:

function foo () {} // FunctionDeclaration

0,function foo () {} // FunctionExpression

The parser knows if it's a FunctionDeclaration or a FunctionExpression, depending on the context where it appears.

In the above example, the second one is an expression because the Comma operator can also handle only expressions.

On the other hand, FunctionDeclarations could actually appear only in what's called "Program" code, meaning code outside in the global scope, and inside the FunctionBody of other functions.

Functions inside blocks should be avoided, because they can lead an unpredictable behavior, e.g.:

if (true) { function foo() { alert('true'); } } else { function foo() { alert('false!'); } } foo(); // true? false? why?

The above code should actually produce a SyntaxError, since a Block can only contain statements (and the ECMAScript Specification doesn't define any function statement), but most implementations are tolerant, and will simply take the second function, the one which alerts 'false!'.

The Mozilla implementations -Rhino, SpiderMonkey,- have a different behavior. Their grammar contains a non-standard Function Statement, meaning that the function will be evaluated at run-time, not at parse time, as it happens with FunctionDeclarations. In those implementations we will get the first function defined.

Functions can be declared in different ways, compare the following:

1- A function defined with the Function constructor assigned to the variable multiply:

var multiply = new Function("x", "y", "return x * y;");

2- A function declaration of a function named multiply:

function multiply(x, y) {
    return x * y;
}

3- A function expression assigned to the variable multiply:

var multiply = function (x, y) {
    return x * y;
};

4- A named function expression func_name, assigned to the variable multiply:

var multiply = function func_name(x, y) {
    return x * y;
};

CMS's answer is correct. For an excellent in-depth explanation of function declarations and expressions, see this article by kangax.
This is a great answer. It does seem to be linked intimately with how the source text is parsed- and the structure of the BNF. in your example 3, should I say that it is a function expression because it follows an equals sign, wheras that form is a function declaration/statement when it appears on a line by itself? I wonder what the purpose of that would be- is it just interpreted as a named function declaration, but without a name? What purpose does that serve if you're not assigning it to a variable, naming it, or calling it?
Aha. Very useful. Thanks, CMS. This part of the Mozilla docs that you linked to is especially enlightening: developer.mozilla.org/En/Core_JavaScript_1.5_Reference/…
+1, although you had the closing bracket in the wrong position in the function expression :-)
@GovindRai, No. Function declarations are handled at compile time and a duplicate function declaration overrides the previous declaration. At runtime, the function declaration is already available and in this case, the one that is available is the one that alerts "false". For more info, read you don't know JS
n
natlee75

Even though this is an old question and answer, it discusses a topic that to this day throws many developers for a loop. I can't count the number of JavaScript developer candidates I've interviewed who couldn't tell me the difference between a function declaration and a function expression and who had no clue what an immediately invoked function expression is.

I'd like to mention, though, one very important thing which is that Premasagar's code snippet wouldn't work even if he had given it a name identifier.

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

The reason this wouldn't work is that the JavaScript engine interprets this as a function declaration followed by a completely unrelated grouping operator that contains no expression, and grouping operators must contain an expression. According to JavaScript, the above snippet of code is equivalent to the following one.

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

();

Another thing I'd like to point out that may be of some use to some people is that any name identifier you provide for a function expression is pretty much useless in the context of the code except from within the function definition itself.

var a = function b() {
    // do something
};
a(); // works
b(); // doesn't work

var c = function d() {
    window.setTimeout(d, 1000); // works
};

Of course, using name identifiers with your function definitions is always helpful when it comes to debugging code, but that's something else entirely... :-)


O
Oriol

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 } 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(){})().


Shame this answer is overlooked. Whilst not as comprehensive as the accepted answer, it provides some very useful extra information and deserves more votes
a
asmmahmud

In javascript, this is called Immediately-Invoked Function Expression (IIFE) .

In order to make it a function expression you've to:

enclose it using () place a void operator before it assign it to a variable.

Otherwise it will be treated as function definition and then you won't be able to call/invoke it at the same time by the following way:

 function (arg1) { console.log(arg1) }(); 

The above will give you error. Because you can only invoke a function expression immediately.

This can be achieved couple of ways: Way 1:

(function(arg1, arg2){
//some code
})(var1, var2);

Way 2:

(function(arg1, arg2){
//some code
}(var1, var2));

Way 3:

void function(arg1, arg2){
//some code
}(var1, var2);

way 4:

  var ll = function (arg1, arg2) {
      console.log(arg1, arg2);
  }(var1, var2);

All above will immediately invoke the function expression.


A
Andrei Bozantan

I have just another small remark. Your code will work with a small change:

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

I use the above syntax instead of the more widely spread version:

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

because I didn't manage to get the indentation to work correctly for javascript files in vim. It seems that vim doesn't like the curly braces inside open parenthesis.


So why does this syntax work when you assign the executed result to a variable, but not standalone?
@paislee -- Because the JavaScript engine interprets any valid JavaScript statement beginning with the function keyword as a function declaration in which case the trailing () is interpreted as a grouping operator which, according to JavaScript syntax rules, can only and must contain a JavaScript expression.
@bosonix -- Your preferred syntax works well, but it's a good idea to use either the "more widely spread version" you referenced or the variant where () is enclosed within the grouping operator (the one that Douglas Crockford strongly recommends) for consistency: it's common to use IIFEs without assigning them to a variable, and it's easy to forget to include those wrapping parentheses if you don't use them consistently.
B
Barmar
(function(){
     alert(2 + 2);
 })();

Above is valid syntax because anything passed inside parenthesis is consider as function expression.

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

Above is not valid syntax. Because java script syntax parser looks for function name after function keyword since it doesn't find anything it throws an error.


While your answer is not incorrect the accepted answer already covers all of this in dept.
t
theking2

Perhaps the shorter answer would be that

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

is a function literal that defines an (anonymous) function. An additional ()-pair, which is interpreted as an expression, is not expected at toplevel, only literals.

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

is in an expression statement that invokes an anonymous function.


J
Jude

They can be used with parameters-arguments like

var x = 3; 
var y = 4;

(function(a,b){alert(a + b)})(x,y)

would result as 7


J
Jarkko Hietala

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.


S
Shakespear

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