[javasript] function expression to be called immediately

Keywords: Javascript

preface

When you learn JavaScript, you often encounter the code of self executing anonymous functions. Today, let's mainly think about self executing.

Before we learn more about this, let's talk about the name of "self execution". The name of this function in this article is not necessarily completely correct. It mainly depends on how individuals understand it. Because some people say immediate call and others say automatic execution, you can choose a name according to your own understanding, but I hear many people call it "self execution", But the author said a lot later to convince everyone that it is called "function expression called immediately".

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

What is self execution?

In JavaScript, an execution context will be created when any function is executed, because the variables and functions declared for the function may only be within the function. This context provides a simple way to create free variables or private sub functions when calling the function.

// Because another function is returned in this function, this function can access the free variable i
// All said, this internal function actually has permission to call internal objects.

function makeCounter() {
    // i can only be accessed inside makeCounter
    var i = 0;

    return function () {
        console.log(++i);
    };
}

// Note that counter and counter2 are different instances, each with its own range of i.

var counter = makeCounter();
counter(); // logs: 1
counter(); // logs: 2

var counter2 = makeCounter();
counter2(); // logs: 1
counter2(); // logs: 2

alert(i); // Reference error: i has no defind (because i exists inside makeCounter).

In many cases, we don't need multiple instances of makeCounter. Even in some cases, we don't need to display the return value. OK, look down.

The core of the problem

When you declare a function like function foo() {} or var foo = function() {}, you can realize self-execution by adding a bracket after it, such as foo (). See the code:

// Because you want to add a bracket () after the first declared function below, you can execute it yourself, such as foo(),
// Because foo is just a reference to the expression function() {/ * code * /}
 
var foo = function(){ /* code */ }
 
// ... does it mean that it can be executed automatically with a parenthesis?
 
function(){ /* code */ }(); // SyntaxError: Unexpected token (
//

If the above code is even run, the second code will make an error, because when the parser parses the global function or the function keyword inside the function, it considers the function declaration instead of the function expression by default. If you don't show it to the compiler, it will declare it as a function without a name by default and throw a syntax error message, Because the function declaration requires a name.

Narrator: function, paren, syntax error

Interestingly, even if you add a name to the wrong code above, it will prompt syntax errors, but for different reasons. Adding parentheses () after an expression will execute immediately, but adding parentheses () after a statement is completely different. It is just a grouping operator.

// The following function is grammatically OK, but it is still just a statement
// An error will still be reported after adding parentheses (), because the grouping operator needs to contain an expression
 
function foo(){ /* code */ }(); // SyntaxError: Unexpected token )
 
// But if you pass an expression in parentheses (), no exception will be thrown
// However, the foo function still does not execute
function foo(){ /* code */ }( 1 );
 
// Because it is completely equivalent to the following code, a function declaration is followed by a irrelevant expression: 
function foo(){ /* code */ }
 
( 1 );

You can visit ECMA-262-3 in detail. Chapter 5. Functions For further information.

Self executing function expression

To solve the above problems, it is very simple. We only need to enclose all the code with large parentheses. Because the parentheses () in JavaScript cannot contain statements, at this point, when parsing the function keyword, the parser will parse the corresponding code into a function expression instead of a function declaration.

// The following two parentheses () are executed immediately

(function () { /* code */ } ()); // This is recommended
(function () { /* code */ })(); // But this can also be used

// Because operators such as parenthesis () and & &, XOR and comma of JS eliminate ambiguity in function expression and function declaration
// So once the parser knows that one of them is already an expression, the others are all expressions by default
// However, please pay attention to the explanation in the next chapter

var i = function () { return 10; } ();
true && function () { /* code */ } ();
0, function () { /* code */ } ();

// If you don't care about the return value, or are not afraid of being difficult to read
// You can even prefix function with a unary operator

!function () { /* code */ } ();
~function () { /* code */ } ();
-function () { /* code */ } ();
+function () { /* code */ } ();

// In another case, the new keyword can also be used, but I'm not sure about its efficiency
// http://twitter.com/kuvos/status/18209252090847232

new function () { /* code */ }
new function () { /* code */ } () // If you need to pass parameters, you only need to add parentheses ()

The above-mentioned parentheses are used to eliminate ambiguity. In fact, they are not necessary at all, because the internal expected parentheses are function expressions, but we still use them for the convenience of developers. When you assign these automatically executed expressions to a variable, we see that there are parentheses at the beginning (, you can understand it quickly without pulling the code to the end to see if there are parentheses.

Save state with closures

Like the parameters passed during the execution of ordinary functions, self executing function expressions can also pass parameters in this way, because closures can directly refer to these parameters passed in. Using these lock ed parameters, self executing function expressions can effectively save the state.

// This code is wrong because the variable i has never been locked
// On the contrary, after the loop is executed, we get the value of i when we click
// Because i really get the value at this time
// So no matter which connection is clicked, I am link #10 (if there are 10 a elements) will be displayed in the end

var elems = document.getElementsByTagName('a');

for (var i = 0; i < elems.length; i++) {

    elems[i].addEventListener('click', function (e) {
        e.preventDefault();
        alert('I am link #' + i);
    }, 'false');

}

// This is useful because it is inside the self executing function expression closure
// The value of i exists as a locked index. After the execution of the loop, although the last value of i becomes the total number of a elements (for example, 10)
// However, the lockedindex value inside the closure remains unchanged because it has been executed
// So when you click Connect, the result is correct

var elems = document.getElementsByTagName('a');

for (var i = 0; i < elems.length; i++) {

    (function (lockedInIndex) {

        elems[i].addEventListener('click', function (e) {
            e.preventDefault();
            alert('I am link #' + lockedInIndex);
        }, 'false');

    })(i);

}

// You can also use self executing function expressions in processing functions like the following
// Not outside addEventListener
// But relatively speaking, the above code is more readable

var elems = document.getElementsByTagName('a');

for (var i = 0; i < elems.length; i++) {

    elems[i].addEventListener('click', (function (lockedInIndex) {
        return function (e) {
            e.preventDefault();
            alert('I am link #' + lockedInIndex);
        };
    })(i), 'false');

}

In fact, the lockedindex variable in the above two examples can also be replaced with i, because it does not work with the external i, so there will be no problem. This is also the power of anonymous function + closure.

The difference between self executing anonymous functions and immediately executing function expressions

In this post, we have always called self executing anonymous function, but the author of the original English text has always advocated the use of immediate invoked function expression. The author gives a bunch of examples to explain. OK, let's take a look:

// This is a self executing function. The function executes itself internally and recursively
function foo() { foo(); }

// This is a self executing anonymous function because it is not named
// You must use the arguments.callee attribute to execute your own
var foo = function () { arguments.callee(); };

// This may also be a self executing anonymous function, just the foo tag name references itself
// If you change foo to something else, you will get a used to self execute anonymous function
var foo = function () { foo(); };

// Some people call this a self executing anonymous function (even if it's not), because it doesn't call itself, it just executes immediately.
(function () { /* code */ } ());

// Adding a tag name to the function expression can facilitate debugging
// But it must be named, and the function is no longer anonymous
(function foo() { /* code */ } ());

// Immediately called function expressions (IIFE) can also be self executing, but they may not be commonly used
(function () { arguments.callee(); } ());
(function foo() { foo(); } ());

// In addition, the following code will make mistakes in BlackBerry 5, because in a named function expression, its name is undefined
// Oh, strange
(function foo() { foo(); } ());

I hope some examples here can let you understand what is self execution and what is immediate call.
Note: arguments.callee is in ECMAScript 5 strict mode The is abandoned, so it can't be used in this mode.

Last Narrator: Module mode

When talking about this immediately called function expression, I remember the Module mode again. If you are not familiar with this mode, let's take a look at the code first:

// Create an anonymous function expression that is called immediately
// return is a variable that contains what you want to expose
// The returned variable will be assigned to counter instead of the declared function itself

var counter = (function () {
    var i = 0;

    return {
        get: function () {
            return i;
        },
        set: function (val) {
            i = val;
        },
        increment: function () {
            return ++i;
        }
    };
} ());

// counter is an object with multiple attributes. The embodiment of attributes in the above code is actually a method

counter.get(); // 0
counter.set(3);
counter.increment(); // 4
counter.increment(); // 5

counter.i; // undefined because i is not a property of the return object
i; // Reference error: i is not defined (because i only exists in closures)

For more introduction to Module patterns, please visit my last post: in-depth understanding of JavaScript series (2): comprehensive parsing of Module patterns.

More reading

I hope the above examples can help you understand the function expression of immediate call (that is, what we call self-executing function). If you want to know more about function and Module mode, please continue to visit the websites listed below:

  1. ECMA-262-3 in detail. Chapter 5. Functions. - Dmitry A. Soshnikov
  2. Functions and function scope - Mozilla Developer Network
  3. Named function expressions - Juriy "kangax" Zaytsev
  4. Comprehensive analysis of Module mode - Ben Cherry (translated by uncle)
  5. Closures explained with JavaScript - Nick Morgan

Synchronization and recommendation

This article has been synchronized to the Catalog Index: Deep understanding of JavaScript series

In depth understanding of JavaScript series articles, including original, translation, reprint and other types of articles. If it is useful to you, please recommend and support one to give uncle the power to write.

Posted by StripedTiger on Sun, 03 Oct 2021 18:35:48 -0700