js闭包之我见
很久前的一个问题终于得以解决,内心是无比喜悦的,不多说,先上代码:
function test(){
for(var i=0;i<5;i++){
window.onclick=function(){
alert(i);
}
}
}
8 test()
原意是点击第一下弹出i的值0,点击第二下弹出i的值1,一直到第五次点击弹出4。想象是美好的,现实却不按常规出牌,无论点击多少次,alert出来的值都是5。后来接触了js闭包,才知道原来是它搞的鬼。看了不少相关书籍和博客后,发现其实它也没有想象得那么难。
我理解的闭包是可以访其他函数作用域的函数,在js中,通常通过在一个函数内部定义另一个函数来实现,如上例。提到闭包,应首先理解JavaScript特有的链式作用域,即一个函数作用域链的第一位是该函数本身的活动对象,第二位是包含该函数(即父函数)活动对象的索引,第三位是父函数的父函数活动对象的索引,以此类推,直至全局变量对象。注意,反之不成立,即父函数的作用域不包含子函数变量对象的索引。如图:
在文章开头的例子中,每个匿名函数(循环了五次,相当于五个匿名函数)的作用域链中都保存着test的活动对象,所以它们引用的是同一个变量i。当函数返回后,i的值是5,此时,每个函数都引用着保存变量i的对象,所以在每个函数内部,i的值都是5.
闭包具有两面性,有缺点,也有优点,在上例中因为对闭包使用不当没有达到想要的效果,但在一些特殊的应用中,只有用闭包才能实现相应的功能,接下来,就谈谈闭包的用途(优点)和注意事项(缺点)。
1、用途
- 从外部读取某个函数的局部变量。
子函数可以访问父函数的作用域,那么如果全局作用域需要访问函数的局部变量怎么办?
function myA(){
var a=6;
function myB(){
alert(a);
}
return myB;
}
var result=myA();
result();
在上述代码中,无法直接访问myA内的变量a,但是可以再内部定义一个函数myB,该函数可以访问它的父函数myA的作用域内的变量a,然后将这个函数返回并赋值给result,相当于在全局作用域内调用myB。
- 让某个局部变量始终保存在内存中
js的内存回收机制是这样的:当调用一个函数时,为它的局部变量对象分配相应的内存空间,一旦调用结束,则收回为它分配的内存。但是这样会有一个bug,即若在函数A内定义了函数B,而B也调用了A内的局部变量x,且B随时可能被全局调用,因为A调用完毕后内存被收回,所以B访问不到x。js的解决办法是,在定义一个函数时,把这个函数和它可能使用到的变量(包括自身变量和父函数的变量),统统一起保存起来,构成一个闭包,即便当父函数调用完毕,这些变量占用的内存也不会被收回。只有当该函数不可能被调用时,才会收回这些内存。
若需要随时访问某个局部变量,则可以通过该方法,构建一个闭包,让一个局部变量始终保存在内存中。
2、缺点
- 占用过多的内存
上述js的回收机制说明,只要闭包不被销毁,它的作用域链所占用的内存便不能被释放,若过多地、无用地占用内存,对效率和性能极为不利。
- 内存泄露
由于IE9之前的版本对JavaScript和COM对象使用不同的垃圾回收机制(因为IE9之前中的部分对象不是原生JavaScript对象,如其中的BOM和DOM就是以c++的COM对象实现的,所以回收机制不同),所以只要在IE9之前版本中涉及COM对象,就会导致循环引用,以至于对象内存不能被收回。具体来说就是,如果闭包的作用域中保存着一个HTML元素,则意味着该元素将无法被销毁,例如:
function f1(){
var e=document.getElementById("top");
element.onclick=function(){
alert(e.id);
};
}
上述代码为e元素事件创建了一个闭包,即匿名函数,由于匿名函数包含着对f1活动对象的引用,只要匿名函数存在,e元素的引用次数至少为1,因此它占用的内存便永远不会被收回(IE9对COM对象的垃圾回收机制决定的)。该问题也有相应的解决方法,请读者自行思考。
最后,关于闭包中this对象,需注意,由于匿名函数的作用域具有全局性,所以慎用闭包的this对象。没有用var声明的局部变量也具有全局性。
js闭包之我见的更多相关文章
- javascript作用域和闭包之我见
javascript作用域和闭包之我见 看了<你不知道的JavaScript(上卷)>的第一部分--作用域和闭包,感受颇深,遂写一篇读书笔记加深印象.路过的大牛欢迎指点,对这方面不懂的同学 ...
- js闭包的作用域以及闭包案列的介绍:
转载▼ 标签: it js闭包的作用域以及闭包案列的介绍: 首先我们根据前面的介绍来分析js闭包有什么作用,他会给我们编程带来什么好处? 闭包是为了更方便我们在处理js函数的时候会遇到以下的几 ...
- 大部分人都会做错的经典JS闭包面试题
由工作中演变而来的面试题 这是一个我工作当中的遇到的一个问题,似乎很有趣,就当做了一道题去面试,发现几乎没人能全部答对并说出原因,遂拿出来聊一聊吧. 先看题目代码: function fun(n,o) ...
- Js闭包常见三种用法
Js闭包特性源于内部函数可以将外部函数的活动对象保存在自己的作用域链上,所以使内部函数的可以将外部函数的活动对象占为己有,可以在外部函数销毁时依然存有外部函数内的活动对象内容,这样做的好处是可 ...
- js闭包之初步理解( JavaScript closure)
闭包一直是js中一个比较难于理解的东西,而平时用途又非常多,因此不得不对闭包进行必要的理解,现在来说说我对js闭包的理解. 要理解闭包,肯定是要先了解js的一个重要特性, 回想一下,那就是函数作用域, ...
- (原创)JS闭包看代码理解
<html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="C ...
- js闭包理解
js闭包的作用是使函数外可以访问函数内部的变量,是通过 在函数内部 定义 访问函数内变量 的函数实现的,内部的一个函数产生一个闭包 function a() { var i=0; return fun ...
- js闭包理解实例小结
Js闭包 闭包前要了解的知识 1. 函数作用域 (1).Js语言特殊之处在于函数内部可以直接读取全局变量 <script type="text/javascript"> ...
- Js闭包的用途
本来想总结一点JavaScript中的闭包的一些用法,在查资料的时候发现了一篇很好的文章,就转过来收藏了,下面附上传送门: js闭包的用途 ---------sunlylorn 我们来看看闭包的用途. ...
随机推荐
- jquery获取radio单选框的值
1.获取原有单选框的值 var value=$("input[name='is_setting']:checked").val(); 2.获取重选后的单选框的值 <tr> ...
- SoapUI Script Library
Environment Get active environment via groovy script log.info testRunner.testCase.testSuite.project. ...
- c++11多线程学习笔记之四 生产消费者
#ifndef MY_QUEUE_H__ #define MY_QUEUE_H__ #include<list> #include<mutex> #include<thr ...
- idea使用svn提交时出现错误Warning not all local changes may be shown due to an error
参考于https://www.cnblogs.com/zhujiabin/p/6708012.html 解决方案: 1.File > Settings > Version Control ...
- 记一次web服务模块开发过程
一.前言 之前在分析WCS系统的过程中,也赶上要开发其中的一个模块,用于和AGV系统对接完成一些取货.配盘等任务:在这里将这次模块开发的全过程记录一下,以便自己以后开发时能够更加快速的明白流程. 二. ...
- MATLAB常用函数
Matlab的内部常数 pi 圆周率 exp(1) 自然对数的底数e i 或j 虚数单位 Inf或 inf ...
- webuploader 文件上传总结
$(function() { /** * 初始化Web Uploader */ var uploader = WebUploader.create({ swf : '../mobile/Uploade ...
- python logging 实现的进程安全的文件回滚日志类
python标准库中的logging模块在记录日志时经常会用到,但在实际使用发现它自带的用于本地日志回滚的类 logging.handlers.RotatingFileHandler 在多进程环境下会 ...
- (连通图)Network--POJ--3694
链接: http://poj.org/problem?id=3694 http://acm.hust.edu.cn/vjudge/contest/view.action?cid=82833#probl ...
- Javascript 中函数的 length 属性
每个函数都有一个 length属性 (函数名.length), 表示期望接收的函数的个数(而不是实际接收的参数个数) 它与arguments不同. arguments.length 是表示函数实际接收 ...