JavaScript中的(function(){})()构造是什么?

时间:2011-11-22 14:19:12

标签: javascript iife

我曾经知道这意味着什么,但我现在正在努力......

这基本上是在说document.onload吗?

(function () {

})();

28 个答案:

答案 0 :(得分:769)

这是Immediately-Invoked Function Expression或简称IIFE。它在创建后立即执行。

它与任何事件(例如document.onload)的任何事件处理程序无关 考虑第一对括号中的部分:(function(){})(); ....它是一个常规函数表达式。然后查看最后一对(function(){})();,这通常会添加到表达式中以调用函数;在这种情况下,我们先前的表达。

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

(function(){
    // all your code here
    var foo = function() {};
    window.onload = foo;
    // ...
})();
// foo is unreachable here (it’s undefined)

Guffa建议的更正

  

该函数在创建后立即执行,而不是在解析之后执行。在执行任何代码之前解析整个脚本块。此外,解析代码并不会自动意味着它已被执行,如果例如IIFE在函数内部,那么在调用该函数之前它将不会被执行。

<强>更新 由于这是一个非常受欢迎的话题,值得一提的是,IIFE也可以用ES6's arrow function编写(如Gajus已指出in a comment):

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

答案 1 :(得分:104)

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

就像你将它分配给一个变量,并在之后使用它,只是没有变量:

var f = function () {
};
f();

在jQuery中,您可能会想到一个类似的构造:

$(function(){
});

这是约束ready事件的简短形式:

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

答案 2 :(得分:45)

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

三个更常见的措辞:

// 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.');
}();

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

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

或者,它可以是:

~(function(){})();
void function(){}();
true && function(){ /* code */ }();
15.0, function(){ /* code */ }();

你甚至可以写:

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

答案 3 :(得分:31)

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

(function (local_arg) {
   // anonymous function
   console.log(local_arg);
})(arg);

答案 4 :(得分:26)

这就是说立即执行。

所以,如果我这样做:

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/


第二个例子:

var val = (function(){
     return 13 + 5;
})();

alert(val); //18

答案 5 :(得分:20)

该构造称为立即调用函数表达式(IIFE),这意味着它立即执行。可以把它当作一个在解释器达到该功能时自动调用的函数。

最常见的用例:

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

在下面的示例中,count不会在紧接调用的函数之外可用,即count的范围不会泄漏出函数。如果你试图在立即调用的函数之外访问它,你应该得到Reference Error

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

ES6替代方案(推荐)

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

因此,不是在上面提到的用例中使用IIFE的复杂构造,现在可以编写更多,更简单的代码,以确保变量的作用域不会泄漏到您想要的块之外。

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

在此示例中,我们使用let定义count变量,使count仅限于代码块,我们使用大括号{...}创建。

我称之为Curly Jail

答案 6 :(得分:13)

(function () {
})();

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

使用词法作用域的ECMAScript 6之前的JavaScript,IIFE用于模拟块作用域。 (通过引入let和const关键字,可以使用ECMAScript 6块作用域。) Reference for issue with lexical scoping

Simulate block scoping with IIFE

使用IIFE的性能优势是能够传递常用的全局对象,如窗口,文档等。作为一个参数,通过减少范围查找。(记住Javascript查找本地范围内的属性,然后链接到全局范围)。因此,访问本地范围内的全局对象,减少查找时间,如下所示。

(function (globalObj) {
//Access the globalObj
})(window);

答案 7 :(得分:11)

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

(...)();

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

function() {}

这是一个匿名函数。在构造中使用 var 声明的所有内容只能在同一个构造内部显示,并且不会污染全局命名空间。

答案 8 :(得分:9)

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

要理解JS中的IIFE,让我们分解它:

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

      函数表达式的工作原理:
      - 当JS引擎第一次运行时(执行上下文 - 创建阶段),此函数(在=上面的右侧)不会被执行或存储在内存中。变量问候&#39;已分配&#39;未定义&#39; JS引擎的价值。
      - 在执行期间(执行上下文 - 执行阶段),功能对象即时创建(尚未执行),被分配给&#39;问候&#39;变量,可以使用&#39; greet(&#39; somename&#39;)&#39;来调用它。

      第3。立即调用功能表达式

      示例:

      // IIFE
      var greeting = function(name) {
          return 'Namaste' + ' ' + name;
      }('Santosh')
      
      console.log(greeting)  // Namaste Santosh. 
      

      IIFE如何运作
      - 注意&#39;()&#39;函数声明后立即。每个功能对象都有一个&#39; CODE&#39;附属于它的财产,可以赎回。我们可以使用&#39;()&#39;来调用它(或调用它)。括号。
      - 所以这里,在执行期间(执行上下文 - 执行阶段),创建函数对象并同时执行 - 所以现在,greeting变量不是拥有funtion对象,而是返回值(字符串)

      JS中IIFE的典型用例:

      以下IIFE模式非常常用。

      // IIFE 
      // Spelling of Function was not correct , result into error
      (function (name) {
         var greeting = 'Namaste';
         console.log(greeting + ' ' + name);
      })('Santosh');
      
      • 我们在这里做了两件事。 a)将我们的函数表达式包装在braces()中。这就是告诉语法解析器,放在()内的任何东西都是一个表达式(在本例中是函数表达式),并且是一个有效的代码。
        b)我们正在使用它末尾的()同时调用此函数。

      因此,此功能会同时创建并执行(IIFE)。

      IIFE的重要用例:

      IIFE保证我们的代码安全。
      - 作为一个函数的IIFE具有自己的执行上下文,这意味着在其中创建的所有变量都是该函数的本地变量,并且不与全局执行上下文共享。

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

      // 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帮助我们编写安全代码,使我们不会无意中与全局对象发生冲突。

答案 9 :(得分:6)

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

查看W3Schools explanation of a self-invoking function

  

函数表达式可以“自我调用”。

     

自动调用(启动)自调用表达式,而不是   被召唤。

     

如果表达式是,函数表达式将自动执行   接着是()。

     

您无法自行调用函数声明。

答案 10 :(得分:5)

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

语法的解释是:第一个()括号内的函数是没有名称的函数,通过下一个();括号,你可以理解它在它被调用时被调用被定义为。并且您可以在第二个()括号中传递任何参数,这些参数将在第一个括号中的函数中获取。见这个例子:

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

这里的对象&#39;当你在函数签名中抓取它时,你正在传递的函数可以在函数中访问。

答案 11 :(得分:3)

从这里开始:

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

将它放在一个函数中,不再是全局 - 这是您的主要目标。

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

立即调用该函数 - oops:

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

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

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

您可以不使用功能名称:

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

它不需要比这更复杂。

答案 12 :(得分:2)

自动执行匿名功能。它会在创建后立即执行。

一个有用的简短虚拟示例是:

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);

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

答案 13 :(得分:2)

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

代码

var same_name = 1;

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

console.log(same_name);

生成此输出:

2
1

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

答案 14 :(得分:2)

它是一个函数表达式,代表立即调用函数表达式(IIFE)。 IIFE只是创建后立即执行的功能。因此,在必须等待调用该函数执行该函数的情况下,会立即执行IIFE。让我们以示例的方式构造IIFE。假设我们有一个add函数,该函数将两个整数作为args并返回总和 让我们将添加功能添加到IIFE中,

第1步:定义功能

function add (a, b){
    return a+b;
}
add(5,5);

Step2:通过将整个函数声明包装在括号中来调用函数

(function add (a, b){
    return a+b;
})
//add(5,5);

第3步:要立即调用该函数,只需从调用中删除“添加”文本即可。

(function add (a, b){
    return a+b;
})(5,5);

使用IFFE的主要原因是为了在函数中保留私有范围。在您的JavaScript代码中,您要确保没有覆盖任何全局变量。有时您可能会意外地定义一个覆盖全局变量的变量。让我们以身作则。假设我们有一个名为iffe.html的html文件,body标记内的代码是-

<body>
    <div id = 'demo'></div>
    <script>
        document.getElementById("demo").innerHTML = "Hello JavaScript!";
    </script> 
</body>

好吧,上面的代码将毫无疑问地执行,现在假定您意外或故意清除了一个名为document的变量。

<body>
    <div id = 'demo'></div>
    <script>
        document.getElementById("demo").innerHTML = "Hello JavaScript!";
        const document = "hi there";
        console.log(document);
    </script> 
</body>

您将遇到 SyntaxError (语法错误):重新声明不可配置的全局属性文档。

但是,如果您希望清除变量名documet,则可以使用IFFE来实现。

<body>
    <div id = 'demo'></div>
    <script>
        (function(){
            const document = "hi there";
            this.document.getElementById("demo").innerHTML = "Hello JavaScript!";
            console.log(document);
        })();
        document.getElementById("demo").innerHTML = "Hello JavaScript!";
    </script> 
</body>

输出:

enter image description here

让我们再举一个例子,假设我们有一个像波纹管这样的计算器对象-

<body>
    <script>
        var calculator = {
            add:function(a,b){
                return a+b;
            },
            mul:function(a,b){
                return a*b;
            }
        }
        console.log(calculator.add(5,10));
    </script> 
</body>

好吧,它的工作就像一种魅力,如果我们不小心重新分配了计算器对象的值,该怎么办。

<body>
    <script>
        var calculator = {
            add:function(a,b){
                return a+b;
            },
            mul:function(a,b){
                return a*b;
            }
        }
        console.log(calculator.add(5,10));
        calculator = "scientific calculator";
        console.log(calculator.mul(5,5));
    </script> 
</body>

是的,您最终会遇到TypeError:Calculator.mul不是iffe.html函数

但是在IFFE的帮助下,我们可以创建一个私有范围,在其中我们可以创建另一个变量名计算器并使用它;

<body>
    <script>
        var calculator = {
            add:function(a,b){
                return a+b;
            },
            mul:function(a,b){
                return a*b;
            }
        }
        var cal = (function(){
            var calculator = {
                sub:function(a,b){
                    return a-b;
                },
                div:function(a,b){
                    return a/b;
                }
            }
            console.log(this.calculator.mul(5,10));
            console.log(calculator.sub(10,5));
            return calculator;
        })();
        console.log(calculator.add(5,10));
        console.log(cal.div(10,5));
    </script> 
</body>

输出: enter image description here

答案 15 :(得分:1)

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

考虑下面写在名为iife.js

的文件中的函数
(function(){
       console.log("Hello Stackoverflow!");
   })();

上面的代码将在您加载iife.js后立即执行,并将打印&#39; Hello Stackoverflow!&#39;关于开发者工具&#39;安慰。

有关详细说明,请参阅Immediately-Invoked Function Expression (IIFE)

答案 16 :(得分:1)

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

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];
    }
  }
})();

答案 17 :(得分:0)

  

立即调用的函数表达式(IIFE)是一旦创建就执行的函数。它与任何事件或异步执行都没有关系。您可以定义IIFE,如下所示:

(function() {
     // all your code here
     // ...
})();
  

第一对括号function(){...}将括号内的代码转换为表达式。第二对括号调用表达式产生的函数。

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

答案 18 :(得分:0)

使用自我唤起匿名函数的原因是它们永远不应该被其他代码调用,因为它们“设置”了要调用的代码(同时给函数和变量赋予了范围)。

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

非常酷。

答案 19 :(得分:0)

它称为IIFE-立即调用函数表达式。这是显示其语法和用法的示例。它用于确定变量的使用范围,直到该函数为止。

(function () {
  function Question(q,a,c) {
    this.q = q;
    this.a = a;
    this.c = c;
  }

  Question.prototype.displayQuestion = function() {
    console.log(this.q);
    for (var i = 0; i < this.a.length; i++) {
      console.log(i+": "+this.a[i]);
    }
  }

  Question.prototype.checkAnswer = function(ans) {
    if (ans===this.c) {
      console.log("correct");
    } else {
      console.log("incorrect");
    }
  }

  var q1 = new Question('Is Javascript the coolest?', ['yes', 'no'], 0);
  var q2 = new Question('Is python better than Javascript?', ['yes', 'no', 'both are same'], 2);
  var q3 = new Question('Is Javascript the worst?', ['yes', 'no'], 1);

  var questions = [q1, q2, q3];

  var n = Math.floor(Math.random() * questions.length)

  var answer = parseInt(prompt(questions[n].displayQuestion()));
  questions[n].checkAnswer(answer);
})();

答案 20 :(得分:0)

使用ES6语法(为自己发帖,我一直登陆此页面寻找快速示例)

// simple
const simpleNumber = (() => {
  return true ? 1 : 2
})()

// with param
const isPositiveNumber = ((number) => {
  return number > 0 ? true : false
})(4)

答案 21 :(得分:0)

此功能称为自调用功能。自调用(也称为自执行)函数是无名(匿名)函数,在其定义后立即被调用(调用)。 Read more here

这些函数的作用是在定义函数后立即调用该函数,这样可以节省时间和额外的代码行(与在单独的行中调用相比)。

这里是一个示例:

(function() {
    var x = 5 + 4;
    console.log(x);
})();

答案 22 :(得分:0)

这是为什么要使用它的更深入的解释:

“使用IIFE的主要原因是为了获得数据隐私。由于JavaScript的var将变量作用域包含在其包含的函数中,因此IIFE中声明的任何变量都无法被外界访问。”

http://adripofjavascript.com/blog/drips/an-introduction-to-iffes-immediately-invoked-function-expressions.html

答案 23 :(得分:0)

这里已经有很多好的答案,但这是我的2美分:p


您可以将IIFE(立即调用函数表达式)用于:

  1. 避免污染全局命名空间。

    IIFE(甚至任何常规函数)中定义的变量不会覆盖全局范围内的定义。

  2. 防止外部代码访问代码。

    您只能在IIFE中访问您在IIFE中定义的所有内容。它可以防止代码被外部代码修改。外部代码只能访问作为函数结果或设置为外部变量的值的显式返回。

  3. 避免使用不需要重复使用的命名功能。 尽管可以在IIFE模式中使用命名函数,但是您不必这样做,因为通常不需要重复调​​用它。

  4. 对于Universal Module Definitions,在许多JS库中使用。请查看此question了解详情。


IIFE通常以以下方式使用:

(function(param){
   //code here
})(args);

您可以在匿名函数周围省略括号(),并在匿名函数之前使用void运算符。

void function(param){
   //code here
}(args);

答案 24 :(得分:-1)

通常,JavaScript代码在应用程序中具有全局范围。当我们在其中声明全局变量时,有可能在开发的某些其他区域中使用相同的重复变量用于其他目的。由于这种重复,可能会发生一些错误。所以我们可以通过使用立即调用函数表达式来避免这个全局变量,这个表达式是自执行表达式。当我们在 IIFE 表达式中创建我们的代码时,全局变量将像本地范围和局部变量一样。 / p>

我们可以通过两种方式创建 IIFE

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

OR

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

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

答案 25 :(得分:-1)

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

答案 26 :(得分:-1)

以下代码:

(function () {

})();

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

之所以称为函数表达式,是因为Javascript中的( yourcode )运算符将其强制为表达式。 函数表达式函数声明之间的区别如下:

// declaration:
function declaredFunction () {}

// expressions:

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

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

表达式只是一堆代码,可以将其评估为单个值。对于上述示例中的表达式,此值为单个函数对象

有了一个表达式,该表达式的值等于一个函数对象,然后我们可以立即使用()运算符调用该函数对象。例如:

(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的函数括号内。这样可以减少命名冲突的机会,并使您更不小心地命名它们(例如,您不必给它们加上前缀)。

答案 27 :(得分:-1)

我认为2套括号让它有点混乱,但我在googles例子中看到了另一种用法,他们使用类似的东西,我希望这会帮助你更好地理解:

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

因此,如果未定义windows.app,则会立即执行window.app = {},因此在条件评估期间会为window.app分配{},因此结果为{{ 1}}和app现在变为window.app,因此控制台输出为:

{}