从理论角度:所有的函数。因为它们都在创建的时候就将上层上下文的数据保存起来了。哪怕是简单的全局变量也是如此,因为函数中访问全局变量就相当于是在访问自由变量,这个时候使用最外层的作用域。
从实践角度:以下函数才算是闭包:
♦即使创建它的上下文已经销毁,它仍然存在(比如,内部函数从父函数中返回)
♦在代码中引用了自由变量
闭包实践实际使用的时候,闭包可以创建出非常优雅的设计,允许对funArg上定义的多种计算方式进行定制。 如下就是数组排序的例子,它接受一个排序条件函数作为参数:
[1, 2, 3].sort(function (a, b) {
... // 排序条件
});
同样的例子还有,数组的map方法(并非所有的实现都支持数组map方法,SpiderMonkey从1.6版本开始有支持),该方法根据函数中定义的条件将原数组映射到一个新的数组中:
[1, 2, 3].map(function (element) {
return element * 2;
}); // [2, 4, 6]
使用函数式参数,可以很方便的实现一个搜索方法,并且可以支持无穷多的搜索条件:
someCollection.find(function (element) {
return element.someProperty == 'searchCondition';
});
还有应用函数,比如常见的forEach方法,将funArg应用到每个数组元素:
[1, 2, 3].forEach(function (element) {
if (element % 2 != 0) {
alert(element);
}
}); // 1, 3
顺便提下,函数对象的 apply 和 call方法,在函数式编程中也可以用作应用函数。这里,我们将它们看作是应用函数 —— 应用到参数中的函数(在apply中是参数列表,在call中是独立的参数):
(function () {
alert([].join.call(arguments, ';')); // 1;2;3
}).apply(this, [1, 2, 3]);
闭包还有另外一个非常重要的应用 —— 延迟调用:
var a = 10;
setTimeout(function () {
alert(a); // 10, 一秒钟后
}, 1000);
也可以用于回调函数:
... var x = 10; // only for example xmlHttpRequestObject.onreadystatechange = function () { // 当数据就绪的时候,才会调用; // 这里,不论是在哪个上下文中创建,变量“x”的值已经存在了 alert(x); // 10 }; ..
还可以用于封装作用域来隐藏辅助对象:
var foo = {}; // initialization (function (object) { var x = 10; object.getX = function _getX() { return x; }; })(foo); alert(foo.getX()); // get closured "x" – 10
几个闭包样例例子1:闭包中局部变量是引用而非拷贝
function say667() { // Local variable that ends up within closure var num = 666; var sayAlert = function() { alert(num); } num++; return sayAlert; } var sayAlert = say667(); sayAlert()
因此执行结果应该弹出的667而非666。
例子2:多个函数绑定同一个闭包,因为他们定义在同一个函数内。
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; } } setupSomeGlobals(); // 为三个全局变量赋值 gAlertNumber(); //666 gIncreaseNumber(); gAlertNumber(); // 667 gSetNumber(12); // gAlertNumber(); //12
例子3:当在一个循环中赋值函数时,这些函数将绑定同样的闭包
function buildList(list) { var result = []; for (var i = 0; i < list.length; i++) { var item = 'item' + list[i]; result.push( function() {alert(item + ' ' + list[i])} ); } return result; } function testList() { var fnList = buildList([1,2,3]); // using j only to help prevent confusion - could use i for (var j = 0; j < fnList.length; j++) { fnList[j](); } }
testList的执行结果是弹出item3 undefined窗口三次,因为这三个函数绑定了同一个闭包,而且item的值为最后计算的结果,但是当i跳出循环时i值为4,所以list[4]的结果为undefined.(再具体原因,前面有解释过。)
例子4:外部函数所有局部变量都在闭包内,即使这个变量声明在内部函数定义之后。