JavaScript作用域和闭包

14

我正在努力理解闭包(其中有个笑话)并且我发现了这个:

(function () { /* do cool stuff */ })();

这是如何工作的?将函数放在括号中的目的是什么?之后的空括号为什么要加上?

7个回答

30
这是因为在“cool stuff”中声明的任何变量都不会在全局命名空间中创建。在JavaScript中,任何函数都会创建这样的作用域。假设你有一些想要运行的JavaScript代码。如果你这样做:
var b = 1; 
// stuff using b

而其他一些代码使用b时,它将获得你剩下的值。 (或者更糟糕的是,如果一些其他代码在你的代码运行之前设置了b,然后尝试稍后获取其旧值,那么在此期间你已经更改了它。)

另一方面,如果你有这段代码,它声明并调用一个函数a:

function a() { 
     var b = 1;
}

a();

如果后续代码使用b,则不会看到您的值,因为b是函数内的局部变量。当然,问题在于您仍然在创建全局名称 - 在这种情况下是"a"。因此,我们需要一个没有名称的函数 - 这就是为什么您得到了所描述的代码。它声明了一个没有名称的函数,然后调用它。

不幸的是,您不能只是写:

function() { ... }()

因为这段代码会被解析为一个函数声明语句,并且导致语法错误。通过使用括号包裹函数声明,你可以得到一个函数表达式,之后就可以调用它了。你可以像调用其他函数表达式(比如 a)一样使用第二组括号进行调用。例如,如果函数需要参数,你可以在那里传递它们:

(function(a) { ... })(1)

6

这段代码创建了一个函数,调用它,然后丢弃它。

如果你这样看可能会更清晰:

var throwaway = function(){
    // do cool stuff
};
throwaway();

这样做是为了创建一个私有的命名空间。函数中的代码可以拥有函数和变量,而不必担心与页面中加载的其他代码发生冲突。


3
当然,除了你的函数名称与之冲突外,没有其他问题。 - Jesse Rusak
3
嗯,是的。这就是为什么通常使用匿名函数来完成这个任务。我只是用了一个有名字的函数来展示一个更熟悉的形式,从而完成相同的任务。 - Matthew Marshall
jder 给那些来晚了的人提供了更好的答案。 - Kenan Banks
@Peter Bailey - 我的意思是,throwaway 内部的变量不会冲突,但全局名称 "throwaway" 可能会。 - Jesse Rusak
Jder扩展了我的闭包理解,但Matthew简短而甜美地回答了我的3个问题。谢谢! - chrisdillon
毫不冒犯jder,但他的帖子并没有解释闭包——它只是解释了词法作用域。 - Peter Bailey

2
我最近看到了这篇文章。这种函数定义和调用方式被称为“自执行函数”。
(function(){  //code })();

函数内部的代码将在其定义后立即执行。


1

这个结构意味着声明一个匿名函数并立即运行它。你把代码放在函数体内的原因是因为你在其中定义的变量仅作为函数本地变量而不是全局变量。然而,它们仍然可以被定义在该函数内部的闭包所访问。


1

函数周围的括号表明该函数是一个表达式。括号后面是对函数的调用。

请注意,该函数没有名称。


1
一个闭包的方法是将变量传递给函数:
(function($, var_1, var_2) {
    // use JQuery, var_1 and var_2 as local variables
})($, var_1, var_2);

0
将函数声明放在括号内创建一个表达式,其计算结果为其中的匿名函数。因此,第一个括号内计算的是一个函数。
最后的“空括号”调用定义的函数,因此“// do cool stuff”将立即执行。
这是一种即时执行代码的方法,同时使变量不进入全局范围。
然而,这里所示的内容与闭包无关,至少不是直接相关。闭包是指在父函数已经退出之后维护一个词法作用域的能力。

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