以这种方式将参数传递给匿名函数的目的是什么?

62

可能是重复问题:
JavaScript闭包如何工作?

我在使用Google Closure Compiler时做了一些随意的实验,看看它会做什么。

它将我的一个函数重写成了以下样子:

(function(msg) { console.log(msg); })("Hello World!");​​​​​​​

代码中显示"Hello World"作为参数msg传递给了其前面的匿名函数。我仔细看了一会儿,发现在类似于 jQuery 插件中也有类似的写法:

(function( $ ) {
  ...
})(jQuery); 

对于与$的冲突,现在我更加理解了。但是像这样将参数传递给匿名函数的主要原因或目的是什么?为什么不只是在函数内定义这些参数作为变量呢?编写此类函数是否具有性能或灵活性优势?


对于那段特定的代码,没有真正的原因。你可以只是这样做 console.log("hello");。但通常是为了创建一些本地变量,这些变量仅可由在内部创建并导出的某些函数访问。 - I Hate Lazy
我很想看看你的原始代码是如何得出那个形式的。我敢打赌,如果你启用了ADVANCED_OPTIMIZATIONS,它会完全消除该函数。 - I Hate Lazy
@user1689607 这个函数有些不同,而且比较长。我将它简化以便在 SO 上更清晰地呈现。我更加关注概念而不是具体的例子。 - Kyle Macey
如果问题主要是关于函数参数与变量的区别,那么这肯定不是性能问题。更有可能的是,为了减小文件大小,可以避免使用 var 字符。 - I Hate Lazy
如果你像这样传递全局变量,你将能够在缩小代码时更改它的名称。缩小代码不会重命名全局变量,因此如果有100个出现,则类似"MyGlobalVariable.property"的东西将占用大量空间。如果你将它传递给匿名函数并在内部甚至使用相同的名称,缩小器将更改它的名称,例如将其减少到a,从而减小应用程序的总大小。 - Mike Grabowski
3个回答

22

和作用域相关的另一个重要区别在于以下代码:

(function(msg) { console.log(msg); })("Hello World!");​​​​​​​

在某些情况下,使用此方法比以下方法更清晰,因为可以避免命名空间污染:

var msg = "Hello World!";
console.log(msg);

因为第二段代码在不再需要变量之后会留下它,但可能会干扰代码的其他部分。

当您在任何其他函数之外执行提到的代码时,这尤其重要:在这种情况下,msg 变量将作为全局变量在页面上随处可用。


但是,这与将第二个示例包装在匿名函数中有什么不同呢?变量仍然会在结尾处消失。 - Kyle Macey
我认为他的意思更像是这样:http://jsfiddle.net/cv5Jp/ - Kevin B
@KevinB 对啊,你的fiddle和我发布的函数有什么区别? - Kyle Macey
@ʎǝɔɐɯǝlʎʞ:在我看来,这个参数仍然是有效的。编译器只是选择了(function(msg){ console.log(msg); })("sth")而不是!function(msg){ console.log(msg); }("sth")(function(){ var msg="sth"; console.log(msg); })()等等。这只是一个非常简单的例子,我并不期望它背后有什么魔法,这只是其中一种方法。 - Tadeck
1
@ʎǝɔɐɯǝlʎʞ:好的,现在很明显了。它是关于在全局命名空间中保存不必要的变量:您首先定义b()函数,然后使用一次它就留在全局命名空间中。因此,自然的方法是将该函数更改为匿名的、自调用的函数。在函数体中添加新参数可能会导致代码出现问题,编译并不是那么具有侵入性。 - Tadeck
显示剩余3条评论

0
基本上,将您的代码包装在这个中始终是一个好主意:(function(){/*code*/}());,以防止您的变量与其他人的变量发生冲突。
我认为闭包编译器的主要目的是节省这5个字符:var =

0

这有点取决于上下文。在某些情况下,编译器根本不会尝试内联任何函数(如果作用域函数包含“eval”)。如果这是在全局范围内,并且您正在运行高级模式,则只是内联机会出现在编译器停止尝试内联函数之后(或者内联代码中有错误并且错过了机会)。如果您在高级模式下通过编译器运行示例输出,则会得到以下结果:

console.log("Hello World!");

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