关于闭包:javascript中自执行函数的目的是什么?

What is the purpose of a self executing function in javascript?


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

1
2
3
(function(){
    //Bunch of code...
})();


对此:

1
//Bunch of code...



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


例如:

1
2
3
4
5
6
(function(){
    var foo = 3;
    alert(foo);
})();

alert(foo);


这将首先警告"3",然后在下一个警报上抛出错误,因为未定义foo。



简单化。看起来很正常,几乎让人感到安慰:

1
2
3
4
5
6
7
var userName ="Sean";

console.log(name());

function name() {
  return userName;
}


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


等等......什么?


我的意思是。如果有人输入带有某种重音的角色(例如法语或西班牙语),但我只想要'英语'字符? A-z在我的程序中?嗯......西班牙语'n~'和法语'e /'字符(我已经为这些字符使用了两个字符,但你可以在表达重音的字符中进行精神跳跃),这些字符可以被翻译成为'n'和'e'的基本字符。


所以有一个好人已经写了一个全面的字符转换器,我可以包括在我的网站...我包括它。


一个问题:它有一个名为'name'的函数,与我的函数相同。


这就是所谓的碰撞。我们在相同的范围内声明了两个具有相同名称的函数。我们想避免这种情况。


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


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

1
2
3
4
5
6
7
8
9
10
11
12
13
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;
  }
}


这可能会解决我们的问题。现在所有东西都是封闭的,只能在我们的开启和关闭支架内进行访问。


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


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


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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();


或之前!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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()函数的方法。


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


((){})();


语法像罪一样尴尬。但是,它的工作原理。


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


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

1
2
3
4
5
6
7
8
9
10
(function main() {
  var userName ="Sean";

    console.log(name());

    function name() {
      return userName;
    }
  }
)();


因此,在您阅读的大多数教程中,您现在将使用"匿名自执行"或类似的术语进行轰炸。


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


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


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


Self-invocation (also known as
auto-invocation) is when a function
executes immediately upon its
definition. This is a core pattern and
serves as the foundation for many
other patterns of JavaScript
development.


它是一个伟大的粉丝:)因为:

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


极大 - (为什么你应该说它好?)

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


更多这里。



命名空间。 JavaScript的范围是功能级别的。



我不敢相信没有提到暗示全局的答案。


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


基本上,功能块确保您定义的所有相关"全局变量"仅限于您的程序,它不会保护您不会定义隐式全局变量。 JSHint等可以提供有关如何防御此行为的建议。


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


至于private属性,除非你要创建公共框架或库,否则它们会被高估,但如果你需要实现它们,Douglas Crockford有一些好主意。



是否有参数和"一堆代码"返回一个函数?

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


关闭。 something的值由分配给a的函数使用。 something可能有一些变化的值(for循环)和每次a都有一个新函数。



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


当然,在那里的JS实现的一半,他们无论如何。



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

  • 更好的命名空间管理(避免命名空间污染 - > JS模块)
  • 闭包(模拟私人类成员,如OOP所知)

  • 第一个问题已得到很好的解释。对于第二个,请研究以下示例:

    1
    2
    3
    4
    5
    6
    7
    var MyClosureObject = (function (){
      var MyName = 'Michael Jackson RIP';
      return {
        getMyName: function () { return MyName;},
        setMyName: function (name) { MyName = name}
      }
    }());


    注意1:我们没有为MyClosureObject分配函数,更多的是调用该函数的结果。请注意最后一行中的()


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


    让我们尝试一些实验:


    我可以使用getMyName获取MyName并且它可以工作:

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


    以下巧妙的方法是行不通的:

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


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

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


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



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

    1
    2
    3
    4
    5
    for( var i = 0; i < 10; i++ ) {
      setTimeout(function(){
        console.log(i)
      })
    }


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

    1
    2
    3
    4
    5
    6
    7
    for( var i = 0; i < 10; i++ ) {
      (function(num){
        setTimeout(function(){
          console.log(num)
        })
      })(i)
    }


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



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



    javascript中的自调用函数:


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



    自执行功能用于管理变量的范围。


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


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


    如下所示,您可以访问函数内部的globalvariable变量,并注意在函数体内,局部变量优先于具有相同名称的全局变量。

    1
    2
    3
    4
    5
    6
    7
    8
    var globalvar ="globalvar"; // this var can be accessed anywhere within the script

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

    scope();


    因此,基本上自执行函数允许编写代码而无需考虑如何在其他javascript代码块中命名变量。



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


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



    看起来这个问题已经准备就绪,但无论如何我都会发布我的输入。


    我知道什么时候我喜欢使用自动执行功能。

    1
    2
    3
    4
    5
    6
    7
    var myObject = {
        childObject: new function(){
            // bunch of code
        },
        objVar1: <value>,
        objVar2: <value>
    }


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

    1
    2
    3
    4
    5
    6
    7
    object: {
        childObject: {
            childObject: {<value>, <value>, <value>}
        },
        objVar1: <value>,
        objVar2: <value>
    }


    编码一般有许多模糊的方法可以做很多相同的事情,让你想知道,"为什么要这么麻烦?"但是,新的情况不断涌现,你不能再单独依赖基本/核心原则了。


    1
    2
    3
    4
    5
    6
    7
    (function(){
        var foo = {
            name: 'bob'
        };
        console.log(foo.name); // bob
    })();
    console.log(foo.name); // Reference error


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


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


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



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