523

在javascript中,您希望何时使用此选项:

(函数(){//一堆代码。。。})();

在此基础上:

//一堆代码。。。
8

22答案22

重置为默认值
498

这都是关于变量范围的。默认情况下,在自动执行函数中声明的变量仅可用于自动执行函数内的代码。这使得编写代码时不必考虑其他JavaScript代码块中变量的命名方式。

例如,在评论中提到亚力山大:

(函数(){变量foo=3;console.log(foo);})();console.log(foo);

这将首先记录然后在下一个上抛出错误控制台.log因为foo公司未定义。

9
  • 8
    这也有利于包括Netflix工程师在内的许多人:这只是一个功能。它本身并不是终结的代表。有时,自动插入器会与closure相关的场景结合使用,以完成整洁的工作,但如果您没有看到某些内容会保留在引用中,而引用会被垃圾收集并以非闭包语言消失,那么这与CLOSURES无关。 评论 2012年8月18日0:25
  • 2
    所以这意味着,它主要用于闭包? 评论 2013年2月13日7:33
  • 2
    @AlexanderBird,但这已经发生在函数内部的局部变量中:函数(){var foo=3;警报(foo);};警报(foo);所以我还是不明白 评论 2017年12月3日19:08
  • 4
    如果只是为了确定范围,为什么不直接使用{让foo=3}?
    – 朱利奥
    评论 2020年8月20日6:24
  • @朱利奥这个答案来自2009年。块作用域是后来才引入的
    – 云禅
    评论 2021年3月1日14:39
139

简单。看起来很正常,几乎很舒服:

var userName=“Sean”;console.log(名称());函数名(){return userName;}

然而,如果我在我的页面中包含一个真正方便的javascript库,可以将高级字符转换为它们的底层表示,那会怎么样?

等待。。。什么?

我的意思是,如果有人键入带有某种重音的字符,但我只想在我的程序中使用“英语”字符a-Z?好。。。西班牙语的“ñ”和法语的“é”字符可以翻译成基本字符“n”和“e”。

所以有个好人写了一个全面的字符转换器,我可以把它放在我的网站上。。。我把它包括在内。

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

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

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

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

函数main(){//我们现在在自己的隔音房间里//字符转换器库的name()函数可以存在于//和我们的时间一样。var userName=“Sean”;console.log(名称());函数名(){return userName;}}

这可能解决我们的问题。所有内容都已封闭,只能从左大括号和右大括号中访问。

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

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

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

函数main(){//我们现在在自己的隔音房间里//字符转换器库的name()函数可以存在于//和我们的时间一样。var userName=“Sean”;console.log(name());函数名(){return userName;}}main();

或者之前!

main();函数main(){//我们现在在自己的隔音房间里//字符转换器库的name()函数可以存在于//和我们的时间一样。var userName=“Sean”;console.log(名称());函数名(){return userName;}}

第二个问题:“main”这个名字还没有被使用的可能性有多大。。。非常非常苗条。

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

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

((){})();

语法很难理解。然而,它是有效的。

当您将函数定义括在括号中并包含参数列表(另一组或多个括号!)时,它将充当函数呼叫.

因此,让我们用一些自执行语法再次查看我们的代码:

(函数main(){var userName=“Sean”;console.log(名称());函数名称(){return userName;}})();

所以,在你阅读的大多数教程中,你现在都会被“匿名自我执行”或类似的术语轰炸。

经过多年的专业发展,我强烈地敦促你说出您编写的每个函数用于调试目的。

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

8
  • 4
    谢谢:)我一直在互联网上搜索,试图了解IIFE相对于正常功能在可变隐私方面的优势,而你的答案是最好的。每个人都说,最好的优点之一是IIFE中的变量和函数“最终”是私有的,而普通函数只提供了完全相同的东西。最后我想我是通过你的解释过程得到的。IIFE毕竟只是一个函数,但现在我明白为什么要使用它了。再次感谢! 评论 2016年9月20日10:38
  • 2
    感谢您花时间解释这一点。 评论 2016年9月30日18:27
  • 1
    回答得很好。不过,我对你的最后一点有一个问题——当你建议命名所有函数时,你是说有一种方法可以用自我执行函数来命名,还是建议每个人都先命名函数,然后再调用它?编辑哦,我明白了。这个已经命名了。杜。可能需要指出的是,您正在证明使用命名的自我执行功能的合理性。 评论 2017年4月6日16:50
  • 2
    好吧,我的朋友,这就是我想要的答案:) 评论 2017年12月3日19:13
  • 我总是喜欢两种答案;(1)短小、清脆、切中要害。(2.)一个故事般的解释永远留在你的脑海里。你的答案属于(2.) 评论 2020年6月18日13:11
33

自我调用(也称为auto-invocation)是当函数立即执行定义。这是一个核心模式是许多人的基础其他JavaScript模式发展。

我是它的超级粉丝:)因为:

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

巨大的——(为什么你应该说它很好?)

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

更多在这里.

  • 44
    第1点。怎么用?第2点。这是一个完全不同的最佳实践。第3点。什么功能没有?4,5,6,7. 关联?8.好吧,我想1/8还不错。 评论 2012年8月18日0:19
  • 晚了七年,但就第一点而言。它根本没有减少代码,实际上它在创建函数时至少添加了两行代码。
    – ICW公司
    评论 2017年3月3日15:52
  • 2
    这里唯一的一点是“它提供了一个防止命名冲突的闭包”,其他每一点都是改写这个或false。也许你可以简化你的答案? 评论 2018年12月18日19:37
21

名称间距。JavaScript的作用域是功能级的。

  • 10
    因为我使用了命名空间实例范围界定; 这是一个定义问题-参见示例维基百科:计算机科学中的名称空间(有时也称为名称范围)是一个抽象容器或环境,用于保存唯一标识符或符号(即名称)的逻辑分组。名称空间标识符可以为名称提供上下文(计算机科学中的范围),这些术语有时可以互换使用。 评论 2014年10月1日15:44
  • 5
    Javascript的函数级作用域提供了变量名所在的空间命名空间; 它是一个与名称空间标识符无关的匿名标识符。。。 评论 2014年10月1日15:51
  • 我认为投票会被否决,因为你的评论对初学者来说并没有什么帮助。
    – 马吕斯
    评论 2023年7月9日2:23
15

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

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

第一个问题解释得很好。对于第二个,请学习以下示例:

var MyClosure对象=(函数(){var MyName='Michael Jackson RIP';返回{getMyName:function(){return MyName;},setMyName:函数(名称){MyName=name}}}());

注意1:我们没有将函数分配给MyClosure对象,更多调用该函数的结果.注意()在最后一行。

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

让我们尝试一些实验:

我可以得到我的名字使用获取我的姓名它的工作原理是:

console.log(MyClosureObject.getMyName());//迈克尔·杰克逊RIP

以下天真的方法行不通:

console.log(MyClosureObject.MyName);//未定义的

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

MyClosure Object.setMyName('George Michael RIP');console.log(MyClosureObject.getMyName());//乔治·迈克尔·里普

编辑:在上面的示例中MyClosure对象设计用于不带新的前缀,因此按照惯例它不应该大写。

1
  • 1
    你的答案是我第一次意识到可以用(function(){}())代替问题的语法(function{})();他们似乎取得了同样的结果。 评论 2021年8月17日4:48
14

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

这个(函数(){})()construct不能保护隐式全局变量,这对我来说是更大的问题,请参阅http://yuiblog.com/blog/2006/06/01/global-domination/

基本上,功能块确保您定义的所有依赖的“全局变量”都局限于您的程序,它并不能防止您定义隐式全局变量。JS提示或者类似的东西可以提供如何防范这种行为的建议。

更简洁var应用程序={}语法提供了类似级别的保护,在“public”页面上可能会被包装在功能块中。(请参见Ember.js公司SproutCore公司用于使用此构造的库的真实示例)

就目前而言私有的如果你不创建公共框架或库,那么属性就有点被高估了,但是如果你需要实现它们,道格拉斯·克罗克福德有一些好主意。

1
  • 9
    严格模式防止隐式全局变量。再加上一个自动调用程序,你就可以搞定了。我从来都不理解对私人财产的喧嚣。在func构造函数内声明vars。完成。如果忘记使用“new”关键字的想法让你夜不能寐,那么编写一个工厂函数。再次执行。 评论 2012年8月18日0:04
7

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

当然,在一半的JS实现上,它们都会这样做。

2
  • 4
    这些将是什么实现? 评论 2009年2月26日21:21
  • 1
    任何未以严格模式编写的实现,以及包含导致其全局的隐式var声明。 评论 2012年8月18日0:27
7

是否有参数且“代码束”返回函数?

vara=函数(x){return function(){document.write(x);}}(something);

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

6
  • +1; 我喜欢直截了当的var x=某物;在外部函数中x个作为参数,但是:imo这样更容易阅读。。。 评论 2009年2月26日21:11
  • @Christoph:如果函数创建后“something”的值发生了变化,那么它将使用新值,而不是创建时的值。
    – 斯特什
    评论 2009年2月26日21:22
  • @你从哪里弄来的?据我所知,情况并非如此;在JS中获得真正引用的唯一方法是使用arguments-object,但即使这样也不适用于所有浏览器 评论 2009年2月26日21:50
  • @克里斯托夫:《JavaScript:好的部分》,道格拉斯·克罗克福德(奥莱利)
    – 斯特什
    评论 2009年2月27日4:37
  • @stesch:它不像你描述的那样工作:如果你删除变量,就会使用新值x个并直接取决于词法范围,即document.write(某物)... 评论 2009年2月27日9:56
7

下面是一个自调用匿名函数的实用示例。

对于(var i=0;i<10;i++){setTimeout(函数(){控制台.log(i)})}

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

对于(var i=0;i<10;i++){(函数(num){setTimeout(函数(){控制台.log(num)})})(i)}

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

2
  • 你能解释一下第一组代码发生了什么吗 评论 2017年11月30日11:55
  • 2
    使用代替无功功率,无功功率第一个案例就可以了。 评论 2018年9月21日13:31

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

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

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

void函数(){console.log('boo!');//预期输出:“boo!”}();

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

从上面的例子可以很清楚地看出,IIFE也会影响效率和性能,因为预期只运行一次的函数将被处决一次,然后永远扔进空地。这意味着函数或方法声明不会保留在内存中。

1
  • 很好,我没见过空隙之前。我喜欢它。
    – 弹出。
    评论 2019年12月20日18:40

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

2

首先你必须访问MDN IIFE公司,现在谈谈这个

  • 这是立即调用的函数表达式。因此,当您的javascript文件从HTML调用时,该函数会立即调用。
  • 这可以防止访问IIFE习惯用法中的变量,并污染全局范围。
2

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

变量的作用域是程序中定义变量的区域。

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

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

var globalvar=“globalvar”;//可以在脚本中的任何位置访问此变量函数scope(){警惕(globalvar);var localvar=“localvar”//只能在功能范围内访问}范围();

因此,基本上,一个自动执行的函数允许编写代码,而不必考虑其他javascript代码块中变量的命名方式。

2

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

var测试=(函数(){const alternative=function(){return“错误获取函数”},方法={GetName:替代,GetAge:替代}//如果不满足条件,将返回默认文本//替换为55<44如果(55>44){//功能一方法。GetName=函数(名称){返回名称;};//功能二方法。GetAge=函数(age){回归年龄;};}返回方法;}());//呼叫console.log(Test.GetName(“Yehia”));console.log(Test.GetAge(66));

1
  • 我们可以将其用作库名称空间吗? 评论 2023年10月23日21:22
1

由于Javascript中的函数是一流的对象,因此通过这样定义它,它可以有效地定义一个“类”,就像C++或C#一样。

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

1

javascript中的自调用函数:

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

1
(函数(){变量foo={name:'bob'};console.log(foo.name);//鲍勃})();console.log(foo.name);//参考错误

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

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

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

1

给出一个简单的问题:“在javascript中,您想什么时候使用这个:…”

我喜欢@ken_browning和@sean_holding的答案,但这里还有一个我没有提到的用法:

让red_tree=新节点(10);(异步函数(){for(设i=0;i<1000;i++){等待red_tree.insert(i);}})();console.log('----->red_tree.printInOrder():',red_tree.printInOrder));

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

我不能在声明函数时不使用async关键字就调用await,我不需要命名函数供以后使用,但需要等待插入调用,或者我需要一些其他更丰富的功能(谁知道呢?)。

1

此方法用于闭包。读一下这个链接了解更多关于闭包的信息。

1
  • 1
    虽然此链接可以回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,则仅链接的答案可能会无效-来自审阅 评论 2022年9月18日4:29
0

看起来这个问题已经完全回答好了,但我还是会发表我的意见。

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

var myObject={childObject:新函数(){//一串代码},objVar1:<值>,objVar2:<值>}

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

对象:{子对象:{childObject:{<value>、<value>和<value>}}, objVar1:<值>,objVar2:<值>}

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

0

有三件事。

  1. 当我们不需要函数名时
  2. 当我们想自动运行代码时。
  3. 当我们想要创建上下文以避免变量名冲突和变量共享时。

对于exmaple的顶级初始化,小部件初始化可以通过自调用烦人的函数来完成。例如,

(功能(文档){//此处输入代码})(文件);
-4

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

0

不是你想要的答案吗?浏览标记的其他问题问你自己的问题.