JS - 闭包的讨论
2009-02-26 16:54
337 查看
closure被翻译成“闭包”,感觉这东西被包装的太学术化。下面参考书本和网上资源简单探讨一下(理解不当之处务请留意)。
1、什么是闭包官方的回答:所谓“闭包”,指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。
看了上面的定义,如果你不是高手,我坚信你会和我一样愤怒的质问:这tmd是人话吗?
要理解闭包,还是代码最有说服力啊,上代码:
1 function funcTest()
2 {
3 var tmpNum=100; //私有变量
4
5 //在函数funcTest内定义另外的函数作为funcTest的方法函数
6 function innerFuncTest(
7 {
8 alert(tmpNum); //引用外层函数funcTest的临时变量tmpNum
9 }
return innerFuncTest; //返回内部函数
}
//调用函数
var myFuncTest=funcTest();
myFuncTest();//弹出100
上面代码中,注释已经写的清清楚楚。现在我们可以这么理解“闭包”:在函数体内定义另外的函数作为目标对象的方法函数(示例中就是在函数funcTest内定义另外的函数innerFuncTest作为funcTest的方法函数),而这个对象的方法函数反过来引用外层函数体中的临时变量(闭包是一种间接保持变量值的机制。示例中就是内部函数innerFuncTest引用外层函数funcTest的临时变量tmpNum,这里必须注意,临时变量可以包括外部函数中声明的所有局部变量、参数和声明的其他内部函数)。当其中一个这样的内部函数在包含它们的外部函数之外被调用时,就会形成闭包(示例中,调用函数的时候,myFuncTest实际调用的是innerFuncTest函数,也就是说funcTest的一个内部函数innerFuncTest在funcTest之外被调用,这时就创建了一个闭包)。
JavaScript的变量作用域是通过函数来维护的。举个例子,对于函数:
function add(a,b){
return a+b;
}
而言,当使用不同的参数(不带参数的函数同样如此)调用它时:
var sum1 = add(1,2);
var sum2 = add(3,4);
每次调用都会通过创建一个新的调用对象维护一个新的函数作用域,从而保证了sum1和sum2分别取得相应的 值3和7。
而闭包的原理,也是如此。下面举两个例子,一个是因为闭包导致了问题,而另一个则利用闭包巧妙地通过函数的作用域绑定参数。这两个例子相关的HTML标记片断如下:
<a href=”#” id=”closureExample”>利用闭包的例子(0.5秒后您会看到提示)</a>
<a href=”#” id=”closureExample2″>由于闭包导致问题的例子1</a>
<a href=”#” id=”closureExample3″>由于闭包导致问题的例子2</a>
<a href=”#” id=”closureExample4″>由于闭包导致问题的例子3</a>
例一:因遭遇闭包而导致问题
面标记片断中有4个<a>元素,假设要给后三个指定事件处理程序,使它们在用户单击时报告自己在页面中的顺序,比如:当用户单击第2个链接时,报告“您单击的是第2个链接”。
为此,如果编写下列为后三个链接添加事件处理程序的函数:
function badExample(){
for (var i = 2; i <= 4; i++) {
var el = document.getElementById('closureExample' + i);
el.onclick = function(){
alert('您单击的是第' + i + '个链接');
}
}
}
然后,在页面载入完成后(不然可能会报错)调用该函数:
window.onload = function(){
badExample();
}
此时单击后3个链接,会看到警告框中显示什么信息呢?——全都是“您单击的是第5个链接”。为什么?
因为在badExample()函数中指定给el.onclick的事件处理程序——也就是那个匿名函数是在badExample()函数运行完成后(用户单击链接时)才被调用的。而调用时,需要对变量i求值,解析程序首先会在事件处理程序内部查找,但没有定义。然后,又到外部作用域,即 badExample()函数中查找,此时有定义,但i的值是5(只有i大于5才会停止执行for循环)。因此,就会取得该值——这正是闭包(匿名函数)要使用其外部函数(badExample)作用域中变量的结果。而且,这也是由于匿名函数本身无法传递参数(故而无法维护自己的作用域)造成的。这个例子的问题怎么解决呢?。参考答案:
1 function badExample(){
2 for (var i = 2; i <= 4; i++) {
3 var el = document.getElementById('closureExample' + i);
4 el.onclick = new popNum(i);
5 }
6 }
7 function popNum(oNum){
8 return function(){
9 alert('您单击的是第'+oNum+'个链接');
}
}
例二:利用闭包绑定参数
还是上面的HTML片段,我们要在用户单击第一个链接时延时弹出一个警告框——注意延时——怎么实现?答案是使用setTimeout()函数,这个函数会在指定的毫秒数之后调用一个函数,如:
setTimeout(someFunc,500);
但问题是,无法给其中的someFunc函数传递参数。而使用闭包则可以轻松解决这个问题:
function goodExample(i){
return function(){
alert(i);
};
}
函数goodExample用来返回一个匿名函数(闭包)。而我们可以通过为它传递参数来使返回的匿名函数绑定该参数,如:
var good = goodExample('这个参数是通过闭包绑定的');
而此时,就可以将绑定了参数的good函数传递给setTimeout()实现延时警告了:
setTimeout(good,500) //此时good中已经绑定了参数
最后,与为第一个链接指定事件处理程序结合起来,完整的代码就是:
window.onload = function(){
var el = document.getElementById('closureExample');
if (el) {
var good = goodExample('这个参数是由闭包绑定的');
el.onclick = function(){
setTimeout(good, 500);
}
}
}
1、什么是闭包官方的回答:所谓“闭包”,指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。
看了上面的定义,如果你不是高手,我坚信你会和我一样愤怒的质问:这tmd是人话吗?
要理解闭包,还是代码最有说服力啊,上代码:
1 function funcTest()
2 {
3 var tmpNum=100; //私有变量
4
5 //在函数funcTest内定义另外的函数作为funcTest的方法函数
6 function innerFuncTest(
7 {
8 alert(tmpNum); //引用外层函数funcTest的临时变量tmpNum
9 }
return innerFuncTest; //返回内部函数
}
//调用函数
var myFuncTest=funcTest();
myFuncTest();//弹出100
上面代码中,注释已经写的清清楚楚。现在我们可以这么理解“闭包”:在函数体内定义另外的函数作为目标对象的方法函数(示例中就是在函数funcTest内定义另外的函数innerFuncTest作为funcTest的方法函数),而这个对象的方法函数反过来引用外层函数体中的临时变量(闭包是一种间接保持变量值的机制。示例中就是内部函数innerFuncTest引用外层函数funcTest的临时变量tmpNum,这里必须注意,临时变量可以包括外部函数中声明的所有局部变量、参数和声明的其他内部函数)。当其中一个这样的内部函数在包含它们的外部函数之外被调用时,就会形成闭包(示例中,调用函数的时候,myFuncTest实际调用的是innerFuncTest函数,也就是说funcTest的一个内部函数innerFuncTest在funcTest之外被调用,这时就创建了一个闭包)。
JavaScript的变量作用域是通过函数来维护的。举个例子,对于函数:
function add(a,b){
return a+b;
}
而言,当使用不同的参数(不带参数的函数同样如此)调用它时:
var sum1 = add(1,2);
var sum2 = add(3,4);
每次调用都会通过创建一个新的调用对象维护一个新的函数作用域,从而保证了sum1和sum2分别取得相应的 值3和7。
而闭包的原理,也是如此。下面举两个例子,一个是因为闭包导致了问题,而另一个则利用闭包巧妙地通过函数的作用域绑定参数。这两个例子相关的HTML标记片断如下:
<a href=”#” id=”closureExample”>利用闭包的例子(0.5秒后您会看到提示)</a>
<a href=”#” id=”closureExample2″>由于闭包导致问题的例子1</a>
<a href=”#” id=”closureExample3″>由于闭包导致问题的例子2</a>
<a href=”#” id=”closureExample4″>由于闭包导致问题的例子3</a>
例一:因遭遇闭包而导致问题
面标记片断中有4个<a>元素,假设要给后三个指定事件处理程序,使它们在用户单击时报告自己在页面中的顺序,比如:当用户单击第2个链接时,报告“您单击的是第2个链接”。
为此,如果编写下列为后三个链接添加事件处理程序的函数:
function badExample(){
for (var i = 2; i <= 4; i++) {
var el = document.getElementById('closureExample' + i);
el.onclick = function(){
alert('您单击的是第' + i + '个链接');
}
}
}
然后,在页面载入完成后(不然可能会报错)调用该函数:
window.onload = function(){
badExample();
}
此时单击后3个链接,会看到警告框中显示什么信息呢?——全都是“您单击的是第5个链接”。为什么?
因为在badExample()函数中指定给el.onclick的事件处理程序——也就是那个匿名函数是在badExample()函数运行完成后(用户单击链接时)才被调用的。而调用时,需要对变量i求值,解析程序首先会在事件处理程序内部查找,但没有定义。然后,又到外部作用域,即 badExample()函数中查找,此时有定义,但i的值是5(只有i大于5才会停止执行for循环)。因此,就会取得该值——这正是闭包(匿名函数)要使用其外部函数(badExample)作用域中变量的结果。而且,这也是由于匿名函数本身无法传递参数(故而无法维护自己的作用域)造成的。这个例子的问题怎么解决呢?。参考答案:
1 function badExample(){
2 for (var i = 2; i <= 4; i++) {
3 var el = document.getElementById('closureExample' + i);
4 el.onclick = new popNum(i);
5 }
6 }
7 function popNum(oNum){
8 return function(){
9 alert('您单击的是第'+oNum+'个链接');
}
}
例二:利用闭包绑定参数
还是上面的HTML片段,我们要在用户单击第一个链接时延时弹出一个警告框——注意延时——怎么实现?答案是使用setTimeout()函数,这个函数会在指定的毫秒数之后调用一个函数,如:
setTimeout(someFunc,500);
但问题是,无法给其中的someFunc函数传递参数。而使用闭包则可以轻松解决这个问题:
function goodExample(i){
return function(){
alert(i);
};
}
函数goodExample用来返回一个匿名函数(闭包)。而我们可以通过为它传递参数来使返回的匿名函数绑定该参数,如:
var good = goodExample('这个参数是通过闭包绑定的');
而此时,就可以将绑定了参数的good函数传递给setTimeout()实现延时警告了:
setTimeout(good,500) //此时good中已经绑定了参数
最后,与为第一个链接指定事件处理程序结合起来,完整的代码就是:
window.onload = function(){
var el = document.getElementById('closureExample');
if (el) {
var good = goodExample('这个参数是由闭包绑定的');
el.onclick = function(){
setTimeout(good, 500);
}
}
}
相关文章推荐
- 关于js闭包影响性能的讨论
- js-闭包 代码多示例
- js闭包的用途
- href里的js执行权限不够?大家来讨论一下
- js的闭包
- js 作用域和闭包1
- javascript深入理解js闭包
- js 中的闭包浅谈
- JS 关于原型和闭包的理解
- javascript深入理解js闭包
- js中的闭包
- JS脚本混淆、加密讨论第1/2页
- js闭包实现私有变量和单例模式
- 分享一篇通俗易懂的js闭包文章
- JS中批量给元素绑定事件过程使用闭包解决
- javascript深入理解js闭包
- [js]利用闭包向post回调函数传参数
- JS闭包理解
- 浅谈js作用域和闭包
- 【CSON原创】 和大家一起分析js闭包中一个不常见的陷阱以及一个不常见的技巧