初次接识这个概念是在一本讲 C# 的书上。当时读了之后一知半解,后来也没怎么用它就闲置没管它。
不过最近做前端里面有用到 Javascript 对象里一系列的东西,不得不又去温习了一下闭包。
《Javascript 语言精粹》一书中说“闭包是 Javascript 一系列高级技巧的钥匙”。因为有了闭包,才有了 Javascript 提供可伸缩、可管理作用域的可能。
我只是谈谈闭包,它是 Javascript 中的一种机制;闭包也是大多数编程语言都提供的一种能力,为编程语言的表达能力做了很大的提升。
简而言之,闭包就是使用方法体把正当前处于自己作用域的变量框住,让它得以继续。

考虑这样一段代码:

 

1
2
3
4
5
function doSomething(){    var a='Hello World';
    setTimeout(function(){
          alert(a);
     },2000);
}  

这是一段很简洁的代码,可它却已经使用了闭包。
如果你调用 doSomething 方法,程序会在 2 秒之后弹出提示。不过再细看一下,它还是有一点悬机的。弹出提示的时候,由 setTimeout 调用的那个匿名方法内部没有声明变量 a,它怎么知道 a 是什么呢?按理说,2秒之后, doSomething 方法早已经运行完毕了啊,可结果是确实可以正确地执行:a 的值被保留了下来。
这就是闭包:在这个函数被声明的时候,当时的作用域可以访问到 a 就可以了。

闭包被广泛地运用在对象框架和注册异步回调编程中。
在对象框架中,可以用它来模拟保存局部变量,也就是只有内部方法可以访问到的变量——关于这部分,请参考阮一峰的学习 Javascript 闭包一文
在异步编程中,可以用它来保存一个在回调时可能已经消逝的对象,从而让这个对象继续存在。
最常见的异步编程有在 web 中有异步请求,在虚拟执行环境(JVM 或 .NET)中有多线程处理等,都可以用于闭包。
在 Javascript 中,一个最常见的 Ajax 请求的过程如下:
 

1
2
3
4
5
6
7
// 构造 XmlHttpRequest 对象 xhr 
// 初始化 xhr,定义其各种属性,如 url、请求方式等 
// 下面要给它绑定一个回调,即当 ajax 完成时要执行的任务。 
xhr.onreadystatechange = function(){ 
     doSomething(xhr);         //注意这里
 }
  //发送异步请求

 注意上面标有“注意这里”的那行,在那里使用了闭包,把 xhr 对象本身闭包进了一个匿名函数(即回调函数本身)里,然后在 ajax 回来时,目标位置就可以继续使用 xhr 对象而不用担心此时当前调用位置的 xhr 变量已消失了。我们的回调目标函数可能是这样的:

 

1
2
3
 function doSomething(xhrObj){
     alert('Ajax 回调完成,回调的结果是:\n' + xhrObj.responseText);
 }

我们知道,C# 的多线程模型中,要创建一个新的线程需要使用这样的语句:

System.Theading.Thread t = new System.Theading.Thread(new System.Theading.ThreadStart(taskMethod));

从上述语句可以看出,实际上是使用了一个方法taskMethod(不论是静态或是实例的)作为新建的线程需要执行的任务的入口;但问题就来了,如果我想在执行的时候同时传入一些参数那应该怎么做呢?那看了上面的文字,你也许已经想出来了:使用闭包。taskMethod 是我们能左右的,同样,它所在的作用域也是我们所能左右的,所以就能使用闭包把我们要使用的变量闭包进多线程执行过程中了。示例代码如下:

 

 

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
class Task
{
   public string taskArg1;
   public string taskArg2;
   public void doSomething()
    {
         //多线程要执行的任务
         Console.WriteLine("参数1的值是:{0}",taskArg1);
         Console.WriteLine("参数2的值是:{0}",taskArg2);
    }
}

Task ta = new Task();
ta.taskArg1 = '变量1的值';
ta.taskArg2 = '变量2的值';


 System.Theading.Thread t = new System.Theading.Thread(new System.Theading.ThreadStart(ta.doSomething));

嗯,就是这样的简单,一切问题迎刃而解!

希望有用。