491

在 javascript 中,你什么时候想使用这个:

(function(){
    //Bunch of code...
})();

对此:

//Bunch of code...
4

20 回答 20

461

这都是关于变量范围的。默认情况下,自执行函数中声明的变量仅可用于自执行函数中的代码。这允许编写代码而不用关心变量在其他 JavaScript 代码块中是如何命名的。

例如,正如Alexander在评论中提到的:

(function() {
  var foo = 3;
  console.log(foo);
})();

console.log(foo);

这将首先记录,然后在下一个因为未定义3而引发错误。console.logfoo

于 2009-02-26T20:57:17.333 回答
117

简单化。看起来很正常,几乎令人欣慰:

var userName = "Sean";

console.log(name());

function name() {
  return userName;
}

但是,如果我在我的页面中包含一个非常方便的 javascript 库,它将高级字符转换为它们的基本级别表示怎么办?

等等……什么?

我的意思是,如果有人输入带有某种重音的字符,但我的程序中只需要“英文”字符 AZ?嗯...西班牙文'ñ' 和法文'é' 字符可以翻译成'n' 和'e' 的基本字符。

因此,某个好人编写了一个全面的字符转换器,我可以将其包含在我的站点中……我将其包含在内。

一个问题:它有一个函数,叫做“名称”,与我的函数相同。

这就是所谓的碰撞。我们在同一个作用域中声明了两个同名的函数。我们想避免这种情况。

所以我们需要以某种方式定义我们的代码范围。

在 javascript 中限定代码范围的唯一方法是将其包装在一个函数中:

function main() {
  // We are now in our own sound-proofed room and the 
  // character-converter library's name() function can exist at the 
  // same time as ours. 

  var userName = "Sean";

  console.log(name());

  function name() {
    return userName;
  }
}

这可能会解决我们的问题。现在所有内容都已包含在内,只能从我们的开括号和闭括号内访问。

我们在函数中有一个函数……看起来很奇怪,但完全合法。

只有一个问题。我们的代码不起作用。我们的userName变量永远不会回显到控制台!

我们可以通过在我们现有的代码块之后添加对我们函数的调用来解决这个问题......

function main() {
  // We are now in our own sound-proofed room and the 
  // character-converter libarary's name() function can exist at the 
  // same time as ours. 

  var userName = "Sean";

  console.log(name());

  function name() {
    return userName;
  }
}

main();

还是以前!

main();

function main() {
  // We are now in our own sound-proofed room and the 
  // character-converter libarary's name() function can exist at the 
  // same time as ours. 

  var userName = "Sean";

  console.log(name());

  function name() {
    return userName;
  }
}

第二个问题:尚未使用名称“main”的可能性有多大?……非常非常苗条。

我们需要更多的范围界定。还有一些方法可以自动执行我们的 main() 函数。

现在我们来谈谈自动执行功能(或自执行、自运行等)。

((){})();

语法很笨拙。但是,它有效。

当您将函数定义包装在括号中并包含参数列表(另一个集合或括号!)时,它充当函数调用

所以让我们再看看我们的代码,使用一些自执行的语法:

(function main() {
  var userName = "Sean";
                
    console.log(name());
                
    function name() {
      return userName;
    }
  }
)();

因此,在您阅读的大多数教程中,您现在会被“匿名自动执行”或类似的术语轰炸。

经过多年的专业发展,我强烈建议您命名为调试目的编写的每个函数。

当出现问题时(并且会出现问题),您将在浏览器中检查回溯。当堆栈跟踪中的条目具有名称时,总是更容易缩小代码问题的范围!

非常啰嗦,我希望它有帮助!

于 2015-05-09T08:13:30.930 回答
32

自调用(也称为自动调用)是指函数在其定义后立即执行。这是一个核心模式,是许多其他 JavaScript 开发模式的基础。

我是它的忠实粉丝:),因为:

  • 它将代码保持在最低限度
  • 它强制将行为与表示分离
  • 它提供了一个防止命名冲突的闭包

非常大——(为什么要说它好?)

  • 它是关于一次定义和执行一个函数。
  • 您可以让该自执行函数返回一个值并将该函数作为参数传递给另一个函数。
  • 它有利于封装。
  • 它也适用于块范围。
  • 是的,您可以将所有 .js 文件包含在一个自执行函数中,并且可以防止全局命名空间污染。;)

更多在这里

于 2010-05-19T15:39:56.703 回答
21

命名空间。JavaScript 的作用域是函数级的。

于 2009-02-26T20:56:26.447 回答
14

我不敢相信没有一个答案提到隐含的全局变量。

(function(){})()构造不能防止隐含的全局变量,这对我来说是更大的问题,请参阅http://yuiblog.com/blog/2006/06/01/global-domination/

基本上,功能块确保您定义的所有相关“全局变量”都限制在您的程序中,它不会保护您免受定义隐式全局变量的影响。JSHint等可以提供有关如何防御这种行为的建议。

更简洁的var App = {}语法提供了类似的保护级别,并且在“公共”页面上时可以包装在功能块中。(请参阅Ember.jsSproutCore了解使用此构造的库的真实示例)

private属性而言,除非您正在创建公共框架或库,否则它们有点被高估了,但如果您需要实现它们,Douglas Crockford有一些好主意。

于 2012-04-13T05:42:48.253 回答
11

我已经阅读了所有答案,这里缺少一些非常重要的东西,我会亲吻。有两个主要原因,为什么我需要自执行匿名函数,或者更好地说“立即调用函数表达式 (IIFE) ”:

  1. 更好的命名空间管理(避免命名空间污染 -> JS 模块)
  2. 闭包(模拟私有类成员,从 OOP 中得知)

第一个已经解释得很好。对于第二个,请研究以下示例:

var MyClosureObject = (function (){
  var MyName = 'Michael Jackson RIP';
  return {
    getMyName: function () { return MyName;},
    setMyName: function (name) { MyName = name}
  }
}());

注意 1:我们不是为 分配一个函数MyClosureObject,而是调用该函数的结果。请注意()最后一行。

注意2:关于Javascript中的函数,您还需要了解的是,内部函数可以访问函数的参数和变量,它们是在内部定义的。

让我们尝试一些实验:

我可以MyName使用getMyName它并且它有效:

 console.log(MyClosureObject.getMyName()); 
 // Michael Jackson RIP

以下巧妙的方法行不通:

console.log(MyClosureObject.MyName); 
// undefined

但我可以设置另一个名称并获得预期的结果:

MyClosureObject.setMyName('George Michael RIP');
console.log(MyClosureObject.getMyName()); 
// George Michael RIP

编辑:在上面的示例中,MyClosureObject设计为不带new前缀使用,因此按照惯例,它不应大写。

于 2017-12-05T12:26:08.773 回答
7

范围隔离,也许。这样函数声明中的变量就不会污染外部命名空间。

当然,在一半的 JS 实现中,他们无论如何都会这样做。

于 2009-02-26T20:57:57.987 回答
7

是否有参数并且“一堆代码”返回一个函数?

var a = function(x) { return function() { document.write(x); } }(something);

关闭。的值something被分配给 的函数使用asomething可能有一些不同的值(for循环),并且每次 a 都有一个新功能。

于 2009-02-26T21:03:54.510 回答
5

这是一个自调用匿名函数如何有用的可靠示例。

for( var i = 0; i < 10; i++ ) {
  setTimeout(function(){
    console.log(i)
  })
}

输出:10, 10, 10, 10, 10...

for( var i = 0; i < 10; i++ ) {
  (function(num){
    setTimeout(function(){
      console.log(num)
    })
  })(i)
}

输出:0, 1, 2, 3, 4...

于 2015-11-19T20:55:52.013 回答
3

一个区别是您在函数中声明的变量是本地的,因此当您退出函数时它们会消失,并且它们不会与其他或相同代码中的其他变量冲突。

于 2009-02-26T20:58:04.897 回答
2

自执行函数用于管理变量的范围。

变量的范围是定义它的程序区域。

全局变量具有全局范围;它在您的 JavaScript 代码中随处定义,并且可以从脚本中的任何位置访问,甚至在您的函数中。另一方面,在函数中声明的变量仅在函数体内定义。它们是局部变量,具有局部作用域并且只能在该函数内访问。函数参数也算作局部变量,并且仅在函数体中定义。

如下所示,您可以访问函数内部的全局变量变量,还请注意,在函数体中,局部变量优先于同名的全局变量。

var globalvar = "globalvar"; // this var can be accessed anywhere within the script

function scope() {
    alert(globalvar);
    var localvar = "localvar"; //can only be accessed within the function scope
}

scope(); 

所以基本上一个自执行函数允许编写代码而不用关心变量在其他javascript代码块中是如何命名的。

于 2018-04-24T10:14:21.773 回答
2

简短的回答是: 防止对全球(或更高)范围的污染。

IIFE(立即调用函数表达式)是将脚本编写为插件、附加组件、用户脚本或任何期望与其他人的脚本一起使用的脚本的最佳实践。这可确保您定义的任何变量都不会对其他脚本产生不良影响。

这是编写 IIFE 表达式的另一种方式。我个人更喜欢以下这种方法:

void function() {
  console.log('boo!');
  // expected output: "boo!"
}();

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/void

从上面的例子中很明显,IIFE 也会影响效率和性能,因为预期只运行一次的函数将被执行一次,然后永远转储到 void 中。这意味着函数或方法声明不会保留在内存中。

于 2019-12-20T03:43:16.780 回答
2

首先你必须访问MDN IIFE,现在关于这个的一些要点

  • 这是立即调用的函数表达式。因此,当您的 javascript 文件从 HTML 调用时,此函数会立即调用。
  • 这可以防止访问 IIFE 惯用语中的变量以及污染全局范围。
于 2020-03-11T12:51:59.343 回答
1

由于 Javascript 中的函数是一等对象,通过以这种方式定义它,它有效地定义了一个类似于 C++ 或 C# 的“类”。

该函数可以定义局部变量,并在其中包含函数。内部函数(实际上是实例方法)将可以访问局部变量(实际上是实例变量),但它们将与脚本的其余部分隔离。

于 2009-02-26T20:57:34.570 回答
1

javascript中的自调用函数:

自调用表达式被自动调用(启动),而不被调用。自调用表达式在创建后立即被调用。这主要用于避免命名冲突以及实现封装。在此函数之外无法访问变量或声明的对象。为了避免最小化(filename.min)的问题,请始终使用自执行函数。

于 2015-10-20T13:02:41.513 回答
1
(function(){
    var foo = {
        name: 'bob'
    };
    console.log(foo.name); // bob
})();
console.log(foo.name); // Reference error

实际上,上述函数将被视为没有名称的函数表达式。

用右括号和左括号包装函数的主要目的是避免污染全局空间。

函数表达式内的变量和函数变为私有(即)它们在函数外不可用。

于 2017-03-31T10:14:51.887 回答
1

鉴于您的简单问题:“在 javascript 中,您什么时候想使用这个:...”

我喜欢@ken_browning 和@sean_holding 的答案,但这是我没有看到的另一个用例:

let red_tree = new Node(10);

(async function () {
    for (let i = 0; i < 1000; i++) {
        await red_tree.insert(i);
    }
})();

console.log('----->red_tree.printInOrder():', red_tree.printInOrder());

其中 Node.insert 是一些异步操作。

在我的函数声明中,我不能在没有 async 关键字的情况下调用 await,并且我不需要命名函数供以后使用,但需要等待该插入调用,或者我需要一些其他更丰富的功能(谁知道?) .

于 2020-04-06T01:50:09.033 回答
0

看起来这个问题已经准备好了,但我还是会发布我的意见。

我知道我什么时候喜欢使用自执行函数。

var myObject = {
    childObject: new function(){
        // bunch of code
    },
    objVar1: <value>,
    objVar2: <value>
}

该函数允许我使用一些额外的代码来定义 childObjects 的属性和属性以获得更简洁的代码,例如设置常用变量或执行数学方程;哦!或错误检查。而不是局限于...的嵌套对象实例化语法

object: {
    childObject: {
        childObject: {<value>, <value>, <value>}
    }, 
    objVar1: <value>,
    objVar2: <value>
}

一般来说,编码有很多晦涩难懂的方式来做很多相同的事情,这让你想知道,“为什么要麻烦?” 但是,新情况不断出现,您不能再仅依赖基本/核心原则。

于 2016-12-18T03:26:53.843 回答
0

您可以使用此函数返回值:

var Test = (function (){
        
        
        const alternative = function(){ return 'Error Get Function '},
        methods = {
            GetName: alternative,
            GetAge:alternative
            }
            
            

// If the condition is not met, the default text will be returned
// replace to  55 < 44
if( 55 > 44){



// Function one
methods.GetName = function (name) {
        
        return name;

};

// Function Two

methods.GetAge = function (age) {
        
        return age;

};





}










    return methods;
    
    
    }());
    
    
    
    
    
    
    
    // Call
   console.log( Test.GetName("Yehia") );

    console.log( Test.GetAge(66) );

于 2021-07-28T14:10:17.547 回答
-4

IIRC 它允许您创建私有属性和方法。

于 2009-02-26T20:57:09.267 回答