理解JavaScript的闭包
在JS这块,免不了被问什么是闭包。
从一个常见的循环问题说起。
有一个ul列表, 里面有5个li标签,我希望点击每个li标签的时候,弹出每个li标签对应的索引值(第一个弹出0,第二个弹出1...)。
<ul id="result">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
当我很认真的写出一段代码:
var lis = document.getElementsByTagName('li'), n = lis.length, i = 0;
for(; i < n; i++){
lis[i].onclick = function(){
alert(i);
}
}
蛮高兴的做了点击测试,从第一个li标签开始,弹出"5",第二个、第三个...全部都弹出“5”。什么情况,这不是我想要的结果。出现了问题,就去找问题的原因,当我点击每个li标签的时候,都弹出“5”,说明for循环已经运行完了,变量 i 也跟着循环条件增加到了5,在我点击前,循环已经执行完成。
经过一番思考,重新写了这段代码:
var lis = document.getElementsByTagName('li'), n = lis.length, i = 0;
for(; i < n; i++){
lis[i].index = i;
lis[i].onclick = function(){
alert(this.index);
}
}
把每次循环时变量 i 的值赋值给每个li标签对象的一个属性。很神奇的这段代码做到了我要的结果,点击每个li标签弹出了对应的索引值(第一个弹出0,第二个弹出1...)。这让我想到是因为变量 i 没有被正确的引用,才发生那都弹出5的问题。懵懵懂懂的想到要正确的引用 变量 i 。经过多次写写改改,点击测试,写出了下面这样的代码:
var lis = document.getElementsByTagName('li'), n = lis.length, i = 0;
for(; i < n; i++){
(function(num){
lis[num].onclick = function(){
alert(num);
}
}(i));
}
//或者
var lis = document.getElementsByTagName('li'), n = lis.length, i = 0;
for(; i < n; i++){
lis[i].onclick = function(num){
return function(){
alert(num);
}
}(i);
}
后来半知半解的明白了这是闭包的一种运用,闭包与变量的作用域、变量的生存周期有密切的关系。要理解闭包就要理解变量的作用域、变量的生存周期。好吧,得先了解与变量有关的知识了。
变量的作用域
当在一个函数中声明一个变量的时候,如果我们没有加上关键字 var, 这个变量就是全局变量,加上了关键字var,这个变量就是局部变量,只有在这个函数内部才能访问这个局部变量,在函数外是访问不到的。 函数的参数也是局部变量,只能在函数内部访问。
function a(){
b = 1; //全局变量
var c = 2; //局部变量
}
a();
alert(b); //1
alert(c); //出错 c未定义
定义了一个函数a,里面有个全局变量b,局部变量c。当在函数a外部访问变量b、c,正常弹出了b的值,而变量c是局部变量,没有正常访问,所以出错,提示变量c未定义。
在函数内部,局部变量优先级高于同名的全局变量。当定义一个函数的时候,也会随之创建一个函数作用域。当在函数内部访问一个变量的时候,会在函数内部作用域搜索这个变量,如果函数内部没有这个变量,会在函数外部搜索,直到找到这个名称的变量为止。如果找不到,就会抛出一个错误。
var v = 1;
function a(){
var m = 2;
function b(){
var n = 3;
alert ( m ); // 2
alert ( v ); // 1
}
b();
alelrt ( n ); //出错
}
a();
上面的代码第一次弹出变量m,首先在函数b里查找,但没有找到,继续往外找,在函数a里面找,m的值为2;第二次弹出变量v,同样的函数b里找,没有找到,再在函数a里找,也没有找到,在往外找,找到了v的值为1;第三次弹出变量n,在函数a里面找,没有找到,不能在函数b里面找,因为查找是向上、向外的,不能向下、向内,最后在函数a的外面找,也没有找到,这时就抛出错误,变量n没有定义。
上面的代码有三个作用域,函数b的作用域,函数a的作用域,window全局作用域(最顶层的作用域),这些作用域联合起来,就形成了作用域链。访问变量就是在这个作用域链的一个搜索过程。
变量的生存周期
在javascript中,全局变量拥有很长的生存周期,直到把变量销毁,而局部变量会随着函数调用结束被销毁。
function a(){
v = 1; //全局变量v
alert(v); //1
}
a();
alert(v) //1 全局变量v还存在
function b(){
var i = 2; //局部变量i
alert(i); //2
}
b();
alert(i) //出错 i未定义 局部变量i已经被销毁
上面的代码定义了两个函数,函数a内部定义全局变量v,函数a执行完后全局变量v还存在;函数b内部定义了局部变量i,函数b执行完后局部变量i被销毁。
function c(){
var k = 3; //局部变量k
return function(){
k++;
alert(k);
}
}
var f = c();
f(); //4
f(); //5
f(); //6
上面的代码定义了一个函数c,函数c内部定义了局部变量k,当函数c执行后返回了一个匿名函数,匿名函数访问了局部变量k,变量f的值为这个匿名函数,调用f实际上是调用这个匿名函数。每次调用f(),变量k的值都会增加1。在这里局部变量k没有在函数c执行完后被销毁,反而“活”了下来,它的生存周期延长了。
什么是闭包?
当前作用域总是能够访问外部作用域中的变量, 函数是 JavaScript 中唯一拥有自身作用域的结构, 因此闭包的创建依赖于函数。
1. 一个函数可以引用外部函数的变量,这个函数就可算是一个闭包。
2. 外部函数已经执行完,内部的函数仍可以引用外部函数的变量。这个内部函数就可算是一个闭包。
3. 函数能存储其作用域的变量、能读写当前函数作用域内变量的函数可算是一个闭包。
理解JavaScript的闭包的更多相关文章
- 深入理解JavaScript的闭包特性如何给循环中的对象添加事件
初学者经常碰到的,即获取HTML元素集合,循环给元素添加事件.在事件响应函数中(event handler)获取对应的索引.但每次获取的都是最后一次循环的索引.原因是初学者并未理解JavaScript ...
- 深入理解javascript的闭包
闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现. 一.变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域 ...
- 如何给循环中的对象添加事件--深入理解JavaScript的闭包特性
初学者经常碰到的,即获取HTML元素集合,循环给元素添加事件.在事件响应函数中(event handler)获取对应的索引.但每次获取的都是最后一次循环的索引.原因是初学者并未理解JavaScript ...
- 理解Javascript 的闭包(closure)
要理解闭包的概念先从变量的作用域说去 一.变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域无非就是两种:全局变量和局部变量. Javascript语言的特殊之 ...
- 深入理解JavaScript的闭包特性 如何给循环中的对象添加事件(转载)
原文参考:http://blog.csdn.net/gaoshanwudi/article/details/7355794 初学者经常碰到的,即获取HTML元素集合,循环给元素添加事件.在事件响应函数 ...
- 理解javascript的闭包,原型,和匿名函数及IIFE
理解javascript的闭包,原型,和匿名函数(自己总结) 一 .>关于闭包 理解闭包 需要的知识1.变量的作用域 例1: var n =99; //建立函数外的全局变量 function r ...
- 简单理解javascript的闭包
看过网上关于javascript的闭包的概念和分析,看完之后都是一头雾水,完全不懂,零度我本来就对于概念性的东西很烦躁,没办法,硬着头皮翻阅了很多的资料,总算理清了一点头绪,现在分享给大家,错误之处还 ...
- 【转】理解JavaScript之闭包
闭包(closure)是掌握Javascript从人门到深入一个非常重要的门槛,它是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现.下面写下我的学习笔记~ 闭包-无处不 ...
- 理解 Javascript 的闭包
什么是闭包 闭包是什么?闭包是Closure,这是静态语言所不具有的一个新特性.但是闭包也不是什么复杂到不可理解的东西,简而言之,闭包就是: 闭包就是函数的局部变量集合,只是这些局部变量在函数返回后会 ...
随机推荐
- hdu4185+poj3020(最大匹配+最小边覆盖)
传送门:hdu4185 Oil Skimming 题意:n*n的方格里有字符*和#,只能在字符#上放1*2的板子且不能相交,求最多能放多少个. 分析:直接给#字符编号,然后相邻的可以匹配,建边后无向图 ...
- uva796(求桥数目)
传送门:Critical Links 题意:给出一个无向图,按顺序输出桥. 分析:模板题,求出桥后排个序输出. #include <cstdio> #include <cstring ...
- jdk1.6与1.7垃圾回收
最近项目中遇到了个关于JVM中GC线程数的问题,做一下简单的总结 问题场景: server:均为 sun公司的solaris 系统 CPU 128个 项目8.1时使用的 java版本: jdk1. ...
- 解决IE11无法下载文件的问题
[问题描写叙述] 单击IE底部下载工具栏没反应,点击"另存为"也没反应 [解决方法] 打开IE11,依次打开菜单:Internet 选项 -> 高级 -> 重置,重置完 ...
- 158个JAVA免豆精品资料汇总
附件完整版下载地址: http://down.51cto.com/data/431561 附件部分预览~ java中国移动收费系统[源代码] http://down.51cto.com/data/70 ...
- ecshop 调用其他数据库中的商品
ecshop中修改includes/cls_ecshop.php中第53行 function table($str) { /* if($str=='goods'){ return '`ecshop3' ...
- Linq 导出Excel
var d = db.User; Repeater1.DataSource = d.ToList(); Repeater1.DataBind(); string guid = Guid.NewGuid ...
- hdu5384(trie树)
hdu5384 给定n个字符串Ai 给定m个字符串Bi 问所有的Bi在每个Ai中出现了多少次 很显然,对Bi建Trie图,然后每次用Ai去匹配的时候,不断查找当前匹配串的最长后缀,这样就能找到答案了 ...
- Coder的利器
Coder的利器记载 工作近三年,使用PC快六年,拥抱Mac整一年,投具器石榴裙三年.14年第一次被同事推荐Everything,开启了JeffJade对工具的折腾之旅,并乐此不疲.时去两年,这必然是 ...
- android中的返回键与Activity
我在做应用时遇到一个问题.就是在启动主页面时须要预先载入一些数据.我是在一个载入页中处理完这些数据然后再转到主页面.但当我在主页面中按返回键时,系统会返回载入页面.我不希望载入页在使用完之 ...