Why are parentheses required around JavaScript IIF

2019-01-19 15:37发布

问题:

This question already has an answer here:

  • Explain the encapsulated anonymous function syntax 9 answers

I'm reading up on JavaScript IIFE and so far the understand concept, but I am wondering about the outside parenthesis. Specifically, why are they required? For example,

(function() {var msg='I love JavaScript'; console.log(msg);}());

works great, but

function() {var msg='I love JavaScript'; console.log(msg);}();

generates a syntax error. Why? There are lots of discussions on IIFE, but I'm not seeing a clear explanation about why the parentheses are required.

回答1:

The version of IIFE that is wrapped in parenthesis works, because this marks the declaration of the internal function declaration as an expression.

http://benalman.com/news/2010/11/immediately-invoked-function-expression/

For more detailed explanation please see:

Advanced JavaScript: Why is this function wrapped in parentheses?

HINT:

The invocation operator (()) only works with expressions, not declarations.



回答2:

There are two ways to create functions in JavaScript (well, 3, but let's ignore new Function()). You can either write a function declaration or write a function expression.

A function declaration in itself is a statement and statements by themselves don't return values (let's also ignore how the debugging console or Node.js REPL print return values of statements). A function expression however is a proper expression and expressions in JavaScript returns values that can be immediately used.

Now, you may have seen people saying that the following is a function expression:

var x = function () {};

It may be tempting to conclude that the syntax:

function () {};

is what makes it an expression. But that's wrong. The syntax above is what makes it an anonymous function. And anonymous functions can either be a declaration or an expression. What makes it an expression is this syntax:

var x = ...

That is, everything to the right of an = sign is an expression. Expressions make it easier to write math formulas in programming languages. So in general everywhere that math is expected to be processed is an expression.

Some of the forms of expressions in JavaScript include:

  • everything to the right of an = operator
  • things in braces () that are not function call braces
  • everything to the right of a math operator (+,-,*,/)
  • all the arguments to the ternary operator .. ? .. : ..

When you write:

function () {}

it is a declaration and does not return a value (the declared function). Therefore trying to call the non-result is an error.

But when you write:

(function () {})

it is an expression and returns a value (the declared function) which may be used immediately (for example, may be called or may be assigned).

Note the rules for what counts as expressions above. From that it follows that braces are not the only things that you can use to construct an IIFE. Below are valid ways for constructing IIFEs (because we write function expressions):

tmp=function(){}()

+function(){}()

-function(){}()

0/function(){}()

0*function(){}()

0?0:function(){}()

(function(){}())

(function(){})()

You may actually see one of the above non-standard forms (particularly the + version) in third-party libraries, because they want to save one byte. But I strongly advise you to only use the brace forms (either are fine), because they are widely recognized as IIFEs by other programmers.



回答3:

This will be a long-winded answer, but will give you the necessary background. In JavaScript there are two ways functions can be defined: A function definition (the classical kind)

function foo() {
  //why do we always use
}

and then the more obscure type, a function expression

var bar = function() {
  //foo and bar
};

In essence the same thing is going on at execution. A function object is created, memory is allocated, and an identifier is bound to the function. The difference is in the syntax. The former is itself a statement which declares a new function, the latter is an expression.

The function expression gives us the ability to insert a function any place where a normal expression would be expected. This lends its way to anonymous functions and callbacks. Take for instance

setTimeout(500, function() {
  //for examples
});

Here, the anonymous function will execute whenever setTimeout says so. If we want to execute a function expression immediately, however, we need to ensure the syntax is recognizable as an expression, otherwise we have ambiguity as to whether of not we mean a function expression or statement.

var fourteen = function sumOfSquares() {
  var value = 0;
  for (var i = 0; i < 4; i++)
    value += i * i;
  return value;
}();

Here sumOfSquares is immediately invoked because it can be recognized as an expression. fourteen becomes 14 and sumOfSquares is garbage-collected. In your example, the grouping operator () coerces its content into an expression, therefore the function is an expression and can be called immediately as such.

One important thing to note about the difference between my first foo and bar example though is hoisting. If you don't know what that it is, a quick Google search or two should tell you, but the quick and dirty definition is that hoisting is JavaScript's behavior to bring declarations (variables and functions) to the top of a scope. These declarations usually only hoist the identifier but not its initialized value, so the entire scope will be able to see the variable/function before it is assigned a value.

With function definitions this is not the case, here the entire declaration is hoisted and will be visible throughout the containing scope.

console.log("lose your " + function() {
  fiz(); //will execute fiz
  buzz(); //throws TypeError
  function fiz() {
    console.log("lose your scoping,");
  }
  var buzz = function() {
    console.log("and win forever");
  };
  return "sanity";
}()); //prints "lose your scoping, lose your sanity"