在 Javascript 中,什么是闭包(Closure)

作者: 来源: 日期:2008-7-11

闭包的两个特点:

  1. 作为一个函数变量的一个引用 - 当函数返回时,其处于激活状态。
  2. 一个闭包就是当一个函数返回时,一个没有释放资源的栈区。

例 1


[Ctrl+A 全部选择 提示:您可先修改部分代码,再按运行]

作为一个 Javascript 程序员,应该明白上面的代码就是一个函数的引用。如果你还不明白或者不清楚的话,请先了解一些基本的知识,我这里不再叙述。

上面的代码为什么是一个闭包?

因为 sayHello2 函数里有一个内嵌匿名函数:

sayAlert = function(){ alert(text); }

在Javascript里,如果你创建了一个内嵌函数(如上例),也就是创建了一个闭包。

在 C 或者其它的主流语言中,当一个函数返回后,所有的局部变量将不可访问,因为它们所在的栈已经被消毁。但在 Javascript 里,如果你声明了一个内嵌函数,局部变量将在函数返回后依然可访问。比如上例中的变量 sy,就是引用内嵌函数中的匿名函数 function(){ alert(text); },可以把上例改成这样:


[Ctrl+A 全部选择 提示:您可先修改部分代码,再按运行]

这里也就与闭包的第二个特点相吻合。

例 2


[Ctrl+A 全部选择 提示:您可先修改部分代码,再按运行]

上面的代码中,匿名变量 function() { alert(num); } 中的 num,并不是被拷贝,而是继续引用外函数定义的局部变量—— num 中的值,直到外函数 say667() 返回。

例 3


[Ctrl+A 全部选择 提示:您可先修改部分代码,再按运行]

上例中,gAlertNumber, gIncreaseNumber, gSetNumber 都是同一个闭包的引用,setupSomeGlobals(),因为他们声明都是通过同一个全局调用——setupSomeGlobals()。

你可以通过“生成”,“增加”,“赋值”,“输出值”这三个按扭来查看输出结果。如果你点击“生成”按钮,将创建一个新闭包。也就会重写 gAlertNumber(), gIncreaseNumber(), gSetNumber(5) 这三个函数。

如果理解以上代码后,看下面的例子:

例 4


[Ctrl+A 全部选择 提示:您可先修改部分代码,再按运行]

运行结果:

item3 is undefined
item3 is undefined
item3 is undefined

代码 result.push(function (){ alert(item + ' ' + list[i]) }),

使 result 数组添加了三个匿名函数的引用。这句代码也可以写成:

var p = function (){ alert(item + ' ' + list[i]) };
result.push(p);

关于为什么会输出三次都是 "item 3 is undefined"

在上面的例子 say667() 例子中已经解释过了。

匿名函数 function() {alert(item + ' ' + list[i])}中的 list[i] 并不是经过拷贝,而是对参数 list 的一个引用,直到函数 buildList() 返回为止。遍历完 list(注:list 的最大下标应该是 2)后,i 经过 i++ 变成了 3,list[i] 也就成了 list[3],而 list[3] 本身是没有初始化的,自然也就是 undefined 了。

例 5


[Ctrl+A 全部选择 提示:您可先修改部分代码,再按运行]

在这最后一个例子中,展示如何声明两个不同的闭包。

相关文章