如何在JavaScript中调用自执行函数?

15

当我有一些需要执行多次的代码时,我会将它们封装在一个函数中,这样就不必重复自己。有时候还需要在页面加载时首先执行此代码。目前我是这样做的:

function foo() {
   alert('hello');
}

foo();

我更倾向于用这种方式:

(function foo() {
   alert('hello');
})();

问题是,这只会在页面加载时执行,但如果我尝试使用foo()在后续调用它,它就不起作用了。

我猜这是一个作用域问题,但有没有办法使自执行函数在稍后被调用时仍能正常工作?


1
我不确定这是否是最佳方法。我想过一段时间后会变得非常混乱。 - user377628
3
可能是重复的问题,参考链接:http://stackoverflow.com/questions/6211466/call-immediately-executing-function-from-outside。 - apsillers
也可能是https://dev59.com/5mw15IYBdhLWcg3wxunL的重复问题(在我看来,该问题有更好的答案,但问题的措辞不是完全匹配的)。 - apsillers
@apsillers 这个问题是重复的,但是唯一的答案只是告诉我做我已经在做的事情。 - user967451
@Jake 确实如此;这就是我发布第二个链接的原因。 - apsillers
它将在IE中起作用,因为它将命名函数表达式视为函数声明。这就是为什么建议不要使用命名函数表达式的原因。参考链接 - RobG
4个回答

34
如果您的函数不依赖于返回值,您可以这样做...
var foo = (function bar() {
   alert('hello');
   return bar;
})();   // hello

foo();  // hello

这个使用本地引用bar在命名函数表达式中将函数返回给外部的foo变量。


或者即使它确实有返回值,你也可以将其设置为有条件的...

var foo = (function bar() {
   alert('hello');
   return foo ? "some other value" : bar;
})();   // hello

alert( foo() );  // hello --- some other value

或者可以手动将值分配给变量,而不是返回它...
var foo; 
(function bar() {
   alert('hello');
   foo = bar;
})();   // hello

foo();  // hello

@RobG所指出的那样,某些版本的IE会将标识符泄漏到封闭变量范围内。该标识符将引用您创建的函数的副本。为使您的NFE在IE中更安全,您可以将该引用置为空值。
bar = null;

请注意,标识符仍将遮蔽范围链中具有相同名称的标识符。无法通过使其为空来解决此问题,并且无法删除局部变量,因此请明智选择NFE名称。


@RobG:没错,但这通常是一个无害的问题。事实上,bar泄漏并创建了一个重复的函数,只有在你在封闭作用域中使用该标识符引用更高层次的变量时才会有影响。 - user1106925
我知道这是一个老问题,但你应该在哪里添加 bar = null,并且它是否需要用于所有三个代码片段? - TarranJones
@TarranJones:将其放置在创建“bar”函数的同一变量作用域中。因此,在创建“bar”函数的下一行就可以了。*(意思是在“})();”行之后。)* - user1106925
这对于整洁的代码非常好,但是会不会在浏览器内存和需要下载执行的js文件大小方面使用更多字节呢? - Stefan P
刚刚发现由于命名函数表达式具有自己的私有作用域,因此您可以在其中使用任何函数名称,因此可以只使用单个字母... (function a(...return a; 因此,从文件大小的角度来看,大小差异将是最小的。我这样想是正确的吗? - Stefan P
显示剩余6条评论

5
如果foo()是一个全局函数,即window的属性,则可以这样做:
(window.foo = function() {
   alert('hello');
})();

// at some later time
foo();

第一组括号中的表达式执行赋值操作以创建foo,但同时也会被评估为函数,因此您可以在末尾加上()立即调用它。
即使该函数应返回一个值,此模式也适用。

这个也可以用本地变量来实现,没有任何问题。var foo; (foo = func...})() - user1106925
谢谢@amnotiam。当然你关于局部变量的说法是正确的。不知为何,当我写这个答案时,我试图将其保持为单个语句并避免使用var语句 - 现在我甚至都不记得为什么了。 - nnnnnn

4

如果要从外部调用函数,则该函数必须在外部可见。理想情况下,您可以让自执行函数将其注入到传递的上下文中(在此示例中为this,它引用全局上下文):

(function(context) {
  var foo = function() {
    alert('hello');
  };
  foo();
  context.foo = foo;
})(this);

...

foo();

更有趣的模式可以在这里找到。


1
传递“this”会更有意义,因为在全局代码中它保证引用全局对象,而“window”不能保证引用任何内容,甚至可能未定义。 - RobG
@RobG:是的,它会的 :-) 我只是提供了一个例子,并明确提到了“一些传递的上下文”。由OP决定哪个上下文对他最好。 - Jordão
我注意到jQuery 1.7.2使用(function(window, ... ){...}(window));,所以你和它们处于同一水平(好的还是坏的?) :-) - RobG

1
第二个函数foo是一个函数定义表达式。
根据《JavaScript权威指南》,在函数定义表达式中,只能在函数内部使用该函数的名称进行调用:
“对于函数定义表达式,名称是可选的:如果存在,则名称仅在函数体内引用函数对象。”
它可以用在递归函数定义表达式中。
例如:
var f = function factorial(n) {
    if (n == 0 || n == 1)
        return 1;
    else
        return n * factorial(n-1);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接