闭包(closure)意味着内层的函数可以引用存在于包围它的函数内的变量,即使外围函数的执行已经终止。
// 查找 id 为 'main' 的元素 var obj = document.getElementById("main"); // 修改它的 border 样式 obj.style.border = "1px solid red"; // 初始化一个在一秒后执行的回调函数(callback) setTimeout(function(){ // 它将隐藏此对象 obj.style.display = 'none'; }, 1000); // 一个用于延时显示警告信息的通用函数 function delayedAlert( msg, time ) { // 初始化一个封装的回调函数 setTimeout(function(){ // 它将使用包含本函数的外围函数传入的 msg 变量 alert( msg ); }, time ); } // 用两个参数调用 delayedAlert 函数 delayedAlert( "Welcome!", 2000 );
上面的例子演示了如何利用闭包避免糟糕的代码。displayAlert函数在函数作用域内使用了闭包,解决代码混乱的问题。
在一些函数式程序设计语言中,有一种称为Curry化(currying)的技术。本质上,Curry化是一种通过把多个参数填充到函数体中,实现将函数转换为一个新的经过简化的函数的技术。下面的代码通过向辽宁国外一个函数预填参数而创建了一个新函数。
// 数字求和函数的函数生成器 function addGenerator( num ) { // 返回一个简单的函数,求两个数字的和 // 其中第一个数字来自生成器 return function( toAdd ) { return num + toAdd }; } // addFive 现在包含一个接受单一参数的函数 // 这个函数能求得 5 加上该参数的和 var addFive = addGenerator( 5 ); // 在传入参数 4 时,addFive 函数的结果是 9 alert( addFive( 4 ) == 9 );
闭包还能解决另一个常见的JavaScript编写问题:开发新手经常会声明大量的全局变量,这些变量可能会影响其它的库,从而导致一些问题。通过自执行的匿名函数可以把所有原本属于全局的变量都隐藏起来。
// 创建一个新的匿名函数,作为包装 (function(){ // 变量原本是全局的 var msg = "Thanks for visiting!"; // 将一个新函数绑定到全局对象 window.onunload = function(){ // 这个函数使用了隐藏的 msg 变量 alert( msg ); }; // 关闭匿名函数并执行之 }());
最后,让我们看看使用闭包会遇到的一个问题。闭包允许引用父函数中的变量,但提供的值并非该循环变量创建时的值,而是在父函数范围内的最终值。这样带来的最常见的问题是在for循环中,有一个变量作为循环技术(如变量i),在这个循环里创建了新的函数,利用闭包来引用循环的计数器。问题是,在这个新的闭包函数被调用时,它引用的计数器的值是其最后一次的赋值,而不是期望的那个值。
// 一个 id 为 main 的元素
var obj = document.getElementById(“main”);
// 用于绑定的一个数组
var items = [ “click”, “keypress” ];
// 遍历数组的每个元素
for ( var i = 0; i < items.length; i++ ) {
// 使用一个自执行的匿名函数来激发出作用域
(function(){
// 将一个函数绑定到该元素
obj[ "on" + items[i] ] = function() {
// item[i] 引用本 for 循环上下文所属作用域中的一个父变量
alert( "Thanks for your " + items[i] );
};
})();
}
[/code]
转载请注明:陈童的博客 » JavaScript的语言特性 — 闭包