JavaScript语言教程

Javascript闭包是什么?

  无论Java程序员和C程序员,对于引用传递的理解是基础,在函数语言中,不仅对象引用可以传递,函数也可以如同对象的引用那样传递。

  闭包closure有两个特点:

  • 闭包是一个函数的局部变量 - 函数返回后它还活着
  • 闭包是一个函数返回时但是内存堆栈帧不会被释放,不会被垃圾回收。

  代码如下:

function sayHello(name) {
  var text = 'Hello ' + name; // local variable局部变量
  var sayAlert = function() { alert(text); }
  return sayAlert;
}

var say2=sayHello("Jdon");

say2();

  点按下面按钮预览上面代码的效果:

  上面的sayAlert是一个sayHello函数的局部变量,sayHello返回的是sayAlert,传给了say2,sayAlert 和say2 实际指向也就是Hold的是同一个函数,这个函数在这里是一个匿名函数 function() { alert(text); },因为这个匿名函数被外部Hold住了,所以它不会被垃圾回收,不会从内存中消失。因为这个匿名函数存在sayHello中,是其一部分,因此sayHello也不会消失,因而sayHello中任何元素都不会消失,比如sayHello中一个局部变量var text就不会消失。这点是与我们一般对象的方法认识不同的,只要是对象方法中的局部变量,该方法被调用完就丢弃,从内存中消失。

  何以见得sayHello中一个局部变量var text不会消失呢?因为我们在上面最后一行调用say2()时,并没有任何赋值行为,但是调出的效果还是Hello Jdon,而不是Hello null。因为调用say2(),实际就是执行匿名函数 function() { alert(text); },而alert(text)中调用本地变量text的值,text的值是Hello Jdon。注意的是,这个text属于一旦被赋值就不再改变,是一个不变性质的量,在Java语言中是要用final修饰符的。  

  当你在一个函数中又使用了另外一个函数时,你在创建一个闭包。

深入案例

  为了了解上面闭包造成var text被Hold在内存中的效果,再看源码,有两个函数foo和foo2:

function foo(x) {
  var tmp = 3;
  return function (y) {
    alert(x + y + (++tmp));
  }
}
var bar = foo(2); // bar现在是一个闭包
bar(10);
bar(10);
bar(10);

  上面我们执行了三次bar(10),结果是16 17 和18,每次结果不一样,因为var tmp这个变量一直存在内存,++tmp是每次累计上次的值,自增。所以,结果不断累积+1。

  下面foo2函数不是一个闭包,无论调用几次结果总是16,因为tmp局部变量每次在函数被调用时都是从3开始。

function foo2(x) {
  var tmp = 3;
  function bar2(y) {
    alert(x + y + (++tmp));
  }
  bar2(10);
}
foo2(2);
foo2(2);
foo2(2)

函数是全局变量

  由于一个函数中的局部变量生存周期只是在一个函数体内,如果我们想将这个局部变量变成全局变量,那么通过函数方法实现对这个局部变量的操作即可,按照面向对象的思路来看,这个局部变量实际是一个可变变量,是一个状态,必须通过自身的方法来操作它,而不是直接将其暴露给外界,任由外界作为全局变量蹂躏。

  下面setupSomeGlobals代码是通过几个函数改变其内部变量num,如果你想给num增加,那么调用gIncreaseNumber方法,然后再按一下gAlertNumber按钮,看看结果是否增加。

function setupSomeGlobals() {
  // Local variable that ends up within closure
  var num = 666;
  // Store some references to functions as global variables
  gAlertNumber = function() { alert(num); }
  gIncreaseNumber = function() { num++; }
  gSetNumber = function(x) { num = x; }
}





 

NodeJs入门之事件驱动