JavaScript匿名闭包

19

我已经阅读了很多关于JavaScript闭包的内容。 那些大括号是干什么用的? 在mozilla.org上,它说闭包应该被定义为

(function(){...})();

但在http://www.adequatelygood.com/JavaScript-Module-Pattern-In-Depth.html上,它说闭包函数是

(function(){...}());

两者有什么区别或后者是错误的吗? 最后()的目的是什么?你会在其中放置一些参数吗? 我正在寻找一个好的参考资料。

编辑: 此外,在Mozilla.org上有一个例子。

var makeCounter = function() {
var privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }
  return {
    increment: function() {
      changeBy(1);
    },
    decrement: function() {
      changeBy(-1);
    },
    value: function() {
      return privateCounter;
    }
  }  
};

为什么这个'function'需要分号?如果它在声明之后立即调用,那么在结束分号前应该放置一个()。但是现在没有。


这不是闭包,而是匿名函数。 - zerkms
1
这是一个关于编程的问题:这些自执行匿名函数(也称 IIFE)实现有何不同? - zerkms
1
可能是(...()) vs. (...)() in javascript closures的重复问题。 - Quentin
1
它调用函数。如果没有它,你只会创建一个从未运行过的函数(也无法运行,因为你无法以任何方式引用它)。 - Felix Kling
这个页面上没有人回答主要问题。(function(){...}());(function(){...})(); 是相同的。前者是Douglas Crockford首选的方法,但后者同样有效。 - Chuck Le Butt
4个回答

23

语法

(function(){...})()

这只是一个立即调用的匿名函数。无论您如何使用括号,底层代码都是声明和调用的函数。

而闭包是用来描述函数可以访问其作用域之外声明的变量的情况,这些变量通过闭包可访问。

为了清楚起见:

如果我们有以下函数:

   function hello() {
      alert("Hello");
   }

我们可以使用以下方式调用该函数

hello()

调用函数'hello'。但如果我们不想给它命名,但仍然要调用它,那么我们可以这样做:

(function hello() {
   alert("Hello");
})()

这将完全与以前调用hello的示例相同。

然而,在这种情况下,给函数命名为“hello”没有意义,因此我们可以简单地将其删除:

(function() {
    alert("Hello");
})()

你原始问题中使用的是哪种符号表示法。


我相信如此 - 我的意思是调用函数表达式的两种方法是相同的。而闭包与立即调用的函数表达式是不同的。如果我误解了问题,请告诉我 :) - AlanFoster
有没有关于调用括号的好文档? - Hoy Cheung
@user1978421 我已经为您添加了一个逐步的示例。 - AlanFoster

8
你的例子展示了一个立即调用函数表达式,或者称作IIFE。它告诉解释器:
  • 这里有一个函数
  • 它没有名字
  • 将它与全局作用域即‘window’分开
  • 现在就调用它

是的,你可以在最后的()中加入参数。例如:

(
    function(username){
        alert("Hello " + username);
    }
)("John Smith")

闭包是javascript的一个特性,它允许我们实现数据隐藏,这大致相当于C++或Java等语言中的私有变量。

function getBmiCalculator(height, weight) {
    // These are private vars
    var height = height;
    var weight = weight;

    function calculateBmi(){
        return weight / (height * height);
    }
    return calculateBmi;
}

var calc = getBmiCalculator(1.85, 90);

// calc still has access to the scope where height and weight live.
var bmi = calc();
alert(bmi);

在这个例子中,只有当calc被销毁时,height和weight才能被垃圾回收。height和weight存在的内存区域或“作用域”被称为“闭包”。

3
没有任何区别。您也可以这样做:
true && function(){ /* code */ }();
0,function(){ /* code */ }();

!function(){ /* code */ }(); // Facebook style
~function(){ /* code */ }();
-function(){ /* code */ }();
+function(){ /* code */ }();

// with new    
new function(){ /* code */ }
new function(){ /* code */ }() // if you need arguments then use brackets

3
为什么?这些都是正确的,但我怀疑一个JavaScript初学者会不会理解为什么这些代码可以起作用。 - Felix Kling
因为在某些情况下,您可以节省1个字符 :) - Ildar
4
这不是我的意思。例如,为什么 (function(){ /* code */ }());+function(){ /* code */ }(); 能够工作,而 function(){ /* code */ }();*function(){ /* code */ }(); 不能?这些代码行有什么特点使得函数能够被调用? - Felix Kling

2

分组运算符可以将函数描述括在没有调用括号的情况下,也可以包括调用括号。即下面两个表达式都是正确的FE:

      (function () {})();
      (function () {}());

函数表达式

(Function Expression)

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