Immediately invoked function expression without us

2020-05-19 04:54发布

问题:

I'm trying to immediately invoke a function without using IIFE pattern (enclosing a function definition inside parentheses). Here I see two scenarios:

  1. When a function declaration is invoked immediately: gives SyntaxError.

  2. When a function expression is invoked immediately: executes successfully.

Example 1: gives SyntaxError

//gives `SyntaxError`
function() {
    console.log('Inside the function');
}();

Example 2: Executes without any error

// Executes without any error
var x = function() {console.log('Inside the function')}(); // Inside the function

So, I have these doubts:

  • With this approach, why does it give an error for function declaration but not for function expression?
  • Is there a way we can immediately invoke a function declaration without using IIFE pattern?

回答1:

In your code you don't have name for the function that's the reason for syntax error. Even if you would had name it would have thrown error.

function func(){
  console.log('x')
}();

The reason is the function declaration doesn't return the values of the function however when you wrap function declaration inside () it forces it be a function expression which returns a value.

In the second example the function() {console.log('Inside the function')} is considered expression because it's on RightHandSide. So it executes without an error.

Is there a way we can immediately invoke a function declaration without using IIFE pattern

You can use + which will make function declaration an expression.

+function(){
  console.log('done')
}()

If you don't want to use + and () you can use new keyword

new function(){
  console.log('done')
}

Extra

A very interesting question is asked by @cat in the comments. I try to answer it.There are three cases

+function(){} //returns NaN
(+function(){return 5})() //VM140:1 Uncaught TypeError: (+(intermediate value)) is not a function
+function(){return 5}() //5

+function(){} returns NaN

+ acts as Unary Plus here which parses the value next to it to number. As Number(function(){}) returns NaN so it also returns NaN

(+function(){return 5;})() returns Error

Usually IIFE are created using (). () are used to make a function declaration an expression + is short way for that. Now +function(){} is already an expression which returns NaN. So calling NaN will return error. The code is same as

Number(function(){})()

+function(){return 5;}() returns 5

In the above line + is used to make a statement an expression. In the above example first function is called then + is used on it to convert it to number. So the above line is same as

Number(function(){return 5}())

In the proof of statement "+ runs on after the function is called" Consider the below snippet

console.log(typeof +function(){return '5'}());

So in the above snippet you can see the returned value is string '5' but is converted to number because of +



回答2:

A function declaration, like

function foo() {
}

defines (and hoists) the variable name foo as a function in the current scope. A function declaration doesn't evaluate to a value; it just does something, a bit like an if does something (rather than evaluate to a value).

You can only invoke values which are functions, eg

<somevalue>()

where somevalue is a variable name that refers to a function.

Note that function declarations require function names, because otherwise there's no variable name to assign the function to - your original

//gives `SyntaxError`
function() {
    console.log('Inside the function');
}();

throws not because of the () at the end, but because of the lack of a name.

You can put parentheses at the end of a function declaration, as long as there's something inside the parentheses - but these parentheses do not call the function, they evaluate to a value:

function x() {
    console.log('Inside the function');
}(console.log('some expression inside parentheses'));

The interpreter sees these as two separate statements, like

function x() {
    console.log('Inside the function');
}
// completely separate:
(console.log('some expression inside parentheses'));

The inside of the parentheses gets evaluated and then discarded, since it doesn't get assigned to anything.

(Empty parentheses are forbidden because they can't be evaluated to a value, similar to how const foo = () is forbidden)



回答3:

The E in IIFE stands for expression, and without the wrapping parenthesis your function is not evaluated as an expression thus the syntax error.

creating an expression is a way of tricking the interpreter and be able to invoke the function immediatly

(function() {
    console.log('Inside the function');
})();

In your example you have a function statement followed by the grouping operator, but it's syntactically incorrect for two reasons, first it doesn't have a name, and second because the grouping operator must have an expression inside it, infact if you add a valid one the error will disappear, still you won't obtain your desired result.

function foo() {
    console.log('Inside the function');
}();

function foo() {
    console.log('Inside the function');
}(1+2);



回答4:

In order to invoke something, it has to be a function value, a declaration just declares a name, and does not evaluate to the function value itself, hence you cannot invoke it.

A declaration cannot be invoked for the above reason. You have to end up with an expression somehow, either through assignment or grouping (IIFE). So that is a no.

If you give us more context on why you would want to do that, maybe we can help with suggestions.



回答5:

Not sure why you would want to do it, but:

Is there a way we can immediately invoke a function declaration without using IIFE pattern?

Well, if for function declaration you mean using the keyword function as in:

function name() { return this.name; }

As far as I know, no. You need the extra parentheses to tell the runtime not to assign the function to a name variable, if I understand this stuff right.

Now, what you actually can do is to use Function as in:

new Function('console.log("ey there")')();

Which will execute the console.log code. No need for IIFE here. But I don't see how this could be better than an IIFE.



回答6:

you can call in either below ways -

~function(){console.log("hi")}()
!function(){console.log("hi")}()
+function(){console.log("hi")}()
-function(){console.log("hi")}()
(function(){console.log("hi")}());
var i = function(){console.log("hi")}();
true && function(){ console.log("hi") }();
0, function(){ console.log("hi") }();
new function(){ console.log("hi") }
new function(){ console.log("hi") }()