关于iife:javascript中的 (function() { } )() 构造是什么?

What is the (function() { } )() construct in JavaScript?

我以前知道这意味着什么,但我现在正在努力…

这基本上是说document.onload吗?

1
2
3
(function () {

})();


它是一个立即调用的函数表达式,简称为IIFE。它在创建后立即执行。

它与任何事件(如document.onload的事件处理程序)无关。考虑第一对括号内的部分:(function(){})();…它是一个正则函数表达式。然后看最后一对(function(){})();,它通常被添加到一个表达式中来调用一个函数;在本例中,是前面的表达式。

当试图避免污染全局名称空间时,经常使用此模式,因为在IIFE中使用的所有变量(像在任何其他正常函数中一样)在其范围之外都不可见。这就是为什么,您可能会将此构造与window.onload的事件处理程序混淆,因为它通常用于:

1
2
3
4
5
6
7
(function(){
    // all your code here
    var foo = function() {};
    window.onload = foo;
    // ...
})();
// foo is unreachable here (it’s undefined)

Guffa建议的修正:

The function is executed right after it's created, not after it is parsed. The entire script block is parsed before any code in it is executed. Also, parsing code doesn't automatically mean that it's executed, if for example the IIFE is inside a function then it won't be executed until the function is called.

更新因为这是一个非常流行的话题,所以值得一提的是,iife也可以用es6的arrow函数编写(就像gajus在评论中指出的那样):

1
((foo) => foo)('foo value')


它只是一个匿名函数,创建后立即执行。

就好像你把它赋给了一个变量,然后在后面使用它,但没有这个变量:

1
2
3
var f = function () {
};
f();

在jquery中,您可能会想到类似的结构:

1
2
$(function(){
});

这是绑定ready事件的简短形式:

1
2
$(document).ready(function(){
});


立即调用的函数表达式(IIFE)立即调用函数。这只意味着函数在定义完成后立即执行。

还有三个常用词:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Crockford's preference - parens on the inside
(function() {
  console.log('Welcome to the Internet. Please follow me.');
}());

//The OPs example, parentheses on the outside
(function() {
  console.log('Welcome to the Internet. Please follow me.');
})();

//Using the exclamation mark operator
//https://stackoverflow.com/a/5654929/1175496
!function() {
  console.log('Welcome to the Internet. Please follow me.');
}();

如果对其返回值没有特殊要求,那么我们可以写:

1
2
3
4
!function(){}();  // => true
~function(){}(); // => -1
+function(){}(); // => NaN
-function(){}();  // => NaN

或者,它可以是:

1
2
3
4
~(function(){})();
void function(){}();
true && function(){ /* code */ }();
15.0, function(){ /* code */ }();

你甚至可以写:

1
2
new function(){ /* code */ }
31.new function(){ /* code */ }() //If no parameters, the last () is not required


它声明一个匿名函数,然后调用它:

1
2
3
4
(function (local_arg) {
   // anonymous function
   console.log(local_arg);
})(arg);


就是说立即执行。

如果我这样做:

1
2
3
4
5
6
7
8
9
10
var val = (function(){
     var a = 0;  // in the scope of this function
     return function(x){
         a += x;
         return a;
     };
})();

alert(val(10)); //10
alert(val(11)); //21

小提琴:http://jsfiddle.net/maniator/lqvpq/

第二个例子:

1
2
3
4
5
var val = (function(){
     return 13 + 5;
})();

alert(val); //18


该构造被称为立即调用的函数表达式(IIFE),这意味着它将立即执行。把它想象成一个函数,当解释器到达该函数时,它会自动被调用。

最常见的用例:

它最常见的用例之一是限制通过var生成的变量的范围。通过var创建的变量的作用域仅限于一个函数,因此这个构造(它是围绕某些代码的函数包装器)将确保变量作用域不会从该函数中泄漏出来。

在下面的示例中,计数在立即调用的函数之外不可用,即EDOCX1的作用域〔9〕不会泄漏出函数。您应该得到一个Reference Error,如果您试图在立即调用的函数之外访问它。

1
2
3
4
(function () {
    var count = 10;
})();
console.log(count);  // Reference Error: count is not defined

ES6替代方案(推荐)

在ES6中,我们现在可以通过letconst创建变量。它们都是块范围的(不像函数范围的var)。

因此,您现在可以编写非常简单的代码,以确保变量的作用域不会从所需的块中泄漏出来,而不是在上面提到的用例中使用复杂的IIFE结构。

1
2
3
4
{
    let count = 10;
};
console.log(count);  // Reference Error: count is not defined

在这个例子中,我们使用let来定义一个count变量,它使count限于代码块,我们用大括号{...}创建。

我称之为Curly Jail


1
2
(function () {
})();

这称为iife(立即调用的函数表达式)。著名的javascript设计模式之一,是现代模块模式的核心和灵魂。顾名思义,它在创建后立即执行。此模式创建一个独立的或私有的执行范围。

在使用词法作用域的ecmascript 6之前,使用iife模拟块作用域。(使用ecmascript 6,可以通过引入let和const关键字来确定块范围。)词法范围问题参考

用IIFE模拟块范围

使用iife的性能优势在于能够通过减少范围查找来传递常用的全局对象(如窗口、文档等)作为参数。(记住,javascript在本地范围内查找属性,并向上链接到全局范围)。因此,在本地范围内访问全局对象,减少如下查找时间。

1
2
3
(function (globalObj) {
//Access the globalObj
})(window);


不,这个构造只是创建一个命名范围。如果你把它分成几部分,你可以看到你有一个外部的

1
(...)();

这是一个函数调用。在括号内有:

1
function() {}

这是一个匿名函数。构造中用var声明的所有内容只在同一构造中可见,不会污染全局命名空间。


这是在javascript中立即调用的函数表达式:

要了解JS中的iLife,让我们将其分解:

  • 表达式:返回值的东西示例:在chrome控制台中尝试以下操作。这些是JS中的表达式。
  • 1
    2
    3
    4
    a = 10
    output = 10
    (1+3)
    output = 4
  • 函数表达式:例子:
  • 1
    2
    3
    4
    5
    6
    // Function Expression
    var greet = function(name){
       return 'Namaste' + ' ' + name;
    }

    greet('Santosh');

    函数表达式如何工作:-当JS引擎第一次运行时(执行上下文-创建阶段),这个函数(在上面=的右边)不会被执行或存储在内存中。变量"greet"由JS引擎分配"undefined"值。-在执行期间(执行上下文-执行阶段),动态创建函数对象(尚未执行),将其分配给"greet"变量,并且可以使用"greet(‘somename’)调用它。

    三。立即调用的函数表达式:

    例子:

    1
    2
    3
    4
    5
    6
    // IIFE
    var greeting = function(name) {
        return 'Namaste' + ' ' + name;
    }('Santosh')

    console.log(greeting)  // Namaste Santosh.

    我的生活是如何工作的:-在函数声明之后立即注意"()"。每个函数对象都有一个可调用的"code"属性。我们可以使用'()'大括号来调用它(或调用它)。-因此,在执行期间(执行上下文-执行阶段),将创建函数对象并同时执行它-所以现在,问候语变量,而不是函数对象,有它的返回值(字符串)

    iLife在JS中的典型使用案例:

    下面的iLife模式非常常用。

    1
    2
    3
    4
    5
    6
    // IIFE
    // Spelling of Function was not correct , result into error
    (function (name) {
       var greeting = 'Namaste';
       console.log(greeting + ' ' + name);
    })('Santosh');
    • 我们在这里做两件事。a)将函数表达式包装在大括号()中。这将告诉语法分析器()中放置的任何内容都是表达式(在本例中是函数表达式),并且是有效的代码。b)我们同时调用这个函数,并在函数末尾使用()。

    所以这个函数是同时创建和执行的(iife)。

    IIFE的重要用例:

    IIFE保护我们的代码安全。-IIFE是一个函数,它有自己的执行上下文,这意味着它内部创建的所有变量都是该函数的局部变量,不与全局执行上下文共享。

    假设我的应用程序中使用了另一个JS文件(test1.js)和iife.js(见下文)。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // test1.js

    var greeting = 'Hello';

    // iife.js
    // Spelling of Function was not correct , result into error
    (function (name) {
       var greeting = 'Namaste';
       console.log(greeting + ' ' + name);
    })('Santosh');

    console.log(greeting)   // No collision happens here. It prints 'Hello'.

    所以iife帮助我们编写安全代码,这样我们就不会无意中与全局对象发生冲突。


    这是一个自调用的匿名函数。

    查看W3Schools对自调用函数的解释。

    Function expressions can be made"self-invoking".

    A self-invoking expression is invoked (started) automatically, without
    being called.

    Function expressions will execute automatically if the expression is
    followed by ().

    You cannot self-invoke a function declaration.


    这是自调用匿名函数。它在定义时执行。这意味着定义了这个函数,并在定义之后立即调用它自己。

    语法的解释是:第一个()圆括号内的函数是没有名称的函数,下一个();圆括号内的函数可以理解为它是在定义时调用的。你可以在第二个()圆括号中传递任何参数,它将在第一个圆括号中的函数中被抓取。请参见此示例:

    1
    2
    3
    (function(obj){
        // Do something with this obj
    })(object);

    在这里,您正在传递的"object"将在函数内由"obj"访问,因为您正在函数签名中获取它。


    自执行函数通常用于封装上下文并避免名称冲突。在(function()中定义的任何变量都不是全局变量。

    代码

    1
    2
    3
    4
    5
    6
    7
    8
    var same_name = 1;

    var myVar = (function() {
        var same_name = 2;
        console.log(same_name);
    })();

    console.log(same_name);

    生成此输出:

    1
    2
    2
    1

    通过使用此语法,可以避免与JavaScript代码中其他地方声明的全局变量发生冲突。


    从这里开始:

    1
    2
    var b = 'bee';
    console.log(b);  // global

    把它放到一个函数中,它就不再是全局的了——你的主要目标。

    1
    2
    3
    4
    5
    6
    function a() {
      var b = 'bee';
      console.log(b);
    }
    a();
    console.log(b);  // ReferenceError: b is not defined -- *as desired*

    立即调用函数--oops:

    1
    2
    3
    4
    function a() {
      var b = 'bee';
      console.log(b);
    }();             // SyntaxError: Expected () to start arrow function, but got ';' instead of '=>'

    使用括号可避免语法错误:

    1
    2
    3
    4
    (function a() {
      var b = 'bee';
      console.log(b);
    })(); // OK now

    您可以不输入函数名:

    1
    2
    3
    4
    (function () {    // no name required
      var b = 'bee';
      console.log(b);
    })();

    不需要再复杂了。


    自执行匿名函数。它一创建就执行。

    一个简单而虚拟的例子是:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    function prepareList(el){
      var list = (function(){
        var l = [];
        for(var i = 0; i < 9; i++){
         l.push(i);
        }
        return l;
      })();

      return function (el){
        for(var i = 0, l = list.length; i < l; i++){
          if(list[i] == el) return list[i];
        }
        return null;
      };
    }

    var search = prepareList();
    search(2);
    search(3);

    因此,您不必每次都创建一个列表,而是只创建一次(减少开销)。


    另一个用例是memoization,其中缓存对象不是全局的:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    var calculate = (function() {
      var cache = {};
      return function(a) {

        if (cache[a]) {
          return cache[a];
        } else {
          // Calculate heavy operation
          cache[a] = heavyOperation(a);
          return cache[a];
        }
      }
    })();

    IIFE(立即调用的函数表达式)是一个函数,它在脚本加载并消失后立即执行。

    考虑将下面的函数写入名为iife.js的文件中

    1
    2
    3
    (function(){
           console.log("Hello Stackoverflow!");
       })();

    上述代码将在加载iife.js后立即执行,并将打印"hello stackoverflow!"在开发人员工具的控制台上。

    有关详细说明,请参阅立即调用的函数表达式(IIFE)


    An immediately invoked function expression (IIFE) is a function that's executed as soon as it's created. It has no connection with any events or asynchronous execution. You can define an IIFE as shown below:

    1
    2
    3
    4
    (function() {
         // all your code here
         // ...
    })();

    The first pair of parentheses function(){...} converts the code inside the parentheses into an expression.The second pair of parentheses calls the function resulting from the expression.

    IIFE也可以描述为一个自调用匿名函数。它最常见的用法是限制通过var生成的变量的范围,或者封装上下文以避免名称冲突。


    使用自唤醒匿名函数的原因是,它们不应该被其他代码调用,因为它们"设置"了要调用的代码(以及为函数和变量提供范围)。

    换言之,它们就像程序开始时"造类"的程序。在它们被实例化(自动)之后,唯一可用的函数就是匿名函数返回的函数。但是,所有其他的"隐藏"函数以及任何状态(在范围创建期间设置的变量)仍然存在。

    很酷。


    以下代码:

    1
    2
    3
    (function () {

    })();

    被调用为立即调用的函数表达式(IIFE)。

    它被称为函数表达式,因为javascript中的( yourcode )运算符强制它成为表达式。函数表达式和函数声明之间的区别如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // declaration:
    function declaredFunction () {}

    // expressions:

    // storing function into variable
    const expressedFunction = function () {}

    // Using () operator, which transforms the function into an expression
    (function () {})

    表达式只是一组代码,可以计算为单个值。对于上述示例中的表达式,该值是单个函数对象。

    当我们有了一个计算为函数对象的表达式之后,我们就可以用()运算符立即调用函数对象。例如:

    1
    2
    3
    4
    5
    6
    7
    8
    (function() {

      const foo = 10;        // all variables inside here are scoped to the function block
      console.log(foo);

    })();

    console.log(foo);  // referenceError foo is scoped to the IIFE

    为什么这个有用?

    当我们处理大量的代码库和/或导入不同的库时,命名冲突的可能性会增加。当我们在一个IIFE中编写代码的某些部分时,所有的变量和函数名都被限定在IIFE的函数括号内。这减少了命名冲突的机会,让您更加粗心地命名它们(例如,您不必给它们加前缀)。


    通常,javascript代码在应用程序中具有全局范围。当我们在其中声明全局变量时,就有机会在开发的其他一些领域中为其他目的使用相同的重复变量。由于这种重复,可能会发生一些错误。因此,我们可以通过立即调用函数表达式来避免这个全局变量,这个表达式是自执行表达式,当我们使IIFE表达式中的代码成为局部范围和局部变量时。

    有两种方法可以创造生命

    1
    2
    3
    4
    (function () {
       "use strict";
        var app = angular.module("myModule", []);
    }());

    1
    2
    3
    4
    (function () {
       "use strict";
        var app = angular.module("myModule", []);
    })();

    在上面的代码片段中,"var-app"现在是一个局部变量。


    通常,我们不会在将函数写入程序后立即调用它。在非常简单的术语中,当您在函数创建后立即调用它时,它被称为iife——一个花哨的名称。


    我认为这两组括号有点让人困惑,但我在谷歌的例子中看到了另一种用法,它们使用了类似的东西,我希望这能帮助您更好地理解:

    1
    2
    3
    var app = window.app || (window.app = {});
    console.log(app);
    console.log(window.app);

    因此,如果没有定义windows.app,那么立即执行window.app = {},因此在条件评估期间window.app被分配给{},所以结果是appwindow.app现在都变成{},所以控制台输出是:

    1
    2
    Object {}
    Object {}