从循环添加事件谈起对JS闭包的理解
1.引子
相信很多初学js的人,都遇到这样一种情况:想要给一堆按钮添加各自的事件,比如点击第i个按钮时,弹出i这个值。理所当然地,我们会这样写:
var buttons = document.getElementsByTagName("button"); //假设一共有8个按钮
for(var i = 0; i < buttons.length; i++) {
buttons[i].onclick = function() {
alert("我的index是"+i);
}
}
然而结果却与想象中不同。无论点击哪个按钮,弹出的都是最后的一个i的值,为什么呢?
对于这个问题,我自己的理解是这样的:
JS的有两种变量作用域:全局作用域和函数作用域。进行循环之后,i的值变成了8。这里的i是位于全局作用域,因此,每当我们点击任何一个按钮时,调用那个匿名函数,里面的i永远是等于8的,弹出的值自然也等于8了。
那么如何达到我们想要的效果呢?这时候就需要用到闭包了。
2.什么是闭包
网上一个比较好的关于闭包的解释是这样的:
能够读取其他函数内部变量的函数
光看概念是难以理解的,让我们看看下面的一段代码:
function a() {
var i = 0;
function b() {
++i;
alert(i);
}
return b;
}
var c = a();
c(); //
c(); //
在上面这段代码中,b是函数a的内部函数,但b被外部的一个变量c引用着,函数b又引用着变量i。在这种情况下,由于函数a内部的变量i被引用着,即使a这个函数执行完毕,a内部的变量i依然不会被js的垃圾回收机制回收,依然能够被引用。
由此我们也发现了闭包的两大用处:
1.可以读取函数内部的变量(在上面的例子中,我们可以在全局读取到函数a内部的变量)
2.让这些变量的值始终保持在内存中(i被b引用,b又被外部变量c引用,因此i始终在内存中)
看到这里,我们大概也能理解闭包这个定义了——“能够读取其他函数内部变量的函数”。把这句话放在上面的例子中,“能够读取函数a内部变量i的函数c”。
上面提到的,使用闭包可以让变量的值始终保存在内存中,我们不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
3.从垃圾回收机制理解闭包
在Javascript中,如果一个对象不再被引用,那么这个对象就会被GC回收。如果两个对象互相引用,而不再被第3者所引用,那么这两个互相引用的对象也会被回收。因为函数a被b引用,b又被a外的c引用,这就是为什么函数a执行后不会被回收的原因。
4.回到一开始的例子
理解闭包之后,我们就可以将这个东西应用到我们一开始的情况中,我们需要把要弹出的值一直保存在内存中,并保持着引用关系,实现方法如下:
for(var i = 0; i < buttons.length; i++) {
buttons[i].onclick = (function(k) {
return function() {
alert("我的index是" + k);
}
})(i);
}
我们把i当作参数传入一个函数内部,然后在函数内部返回另一个函数,并用buttons[i].onclick引用这个返回的函数,使内部变量k保存在内存中,就能实现我们想要的效果了。
5.总结
我比较喜欢的闭包的一种解释是:某个函数【需要用到的变量在外层的函数里】,这个函数就拉着这个变量组成了闭包(来自知乎)。在上面的例子中,函数就是指b,变量就是指i,而形成的这个闭包,我们赋给了c,我们就可以愉快地利用c去使用这个闭包了。
希望上面的文字也能起到这样提醒作用:除了闭包本身,我们也可以从js的作用域和js的垃圾回收机制去理解它。
本文章部分内容转自:http://www.cnblogs.com/xiangqianjin/p/6595115.html
从循环添加事件谈起对JS闭包的理解的更多相关文章
- JS给元素循环添加事件的问题
<ul> <li>男</li> <li>女</li> <li>老</li> <li>少</li&g ...
- JS闭包的理解及常见应用场景
JS闭包的理解及常见应用场景 一.总结 一句话总结: 闭包是指有权访问另一个函数作用域中的变量的函数 1.如何从外部读取函数内部的变量,为什么? 闭包:f2可以读取f1中的变量,只要把f2作为返回值, ...
- js循环添加事件的问题
1.需求 给下面每个按钮增加事件 <ul id="list"> <li>按钮1</li> <li>按钮2</li> &l ...
- 个人对js闭包的理解
闭包算是前端面试的基础题,但我看了很多关于闭包的文章博客,但感觉很多对于闭包的理想还是有分歧的,现在网上对闭包的理解一般是两种: 有些文章认为闭包必须要返回嵌套函数中里面用到外面函数局部变量的方法 ...
- 【闭包】JS闭包深入理解
先看题目代码: 1 2 3 4 5 6 7 8 9 10 11 12 function fun(n,o) { console.log(o) return { fun:function(m){ ...
- js循环添加事件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- JS闭包机制实现为DOM元素循环添加事件
HTML代码: <button type='button' class='btn' id='1'>按钮1</button> <button type='button' c ...
- 谈一谈你对js线程的理解
js线程:javascript是单线程的,所有任务都需要排队,这些任务分为同步任务和异步任务,单线程上有一个主线程任务.同步任务必须再主线程上排队进行,而异步任务(类似于点击事件)必须在主线程上的任务 ...
- 浅谈对Js闭包的理解
理解Js的闭包,首先让我们先看几个概念 执行环境(executive environment)每个函数都有自己的执行环境,匿名函数默认为全局环境. 作用域链(scope chain)子函数继承父函数, ...
随机推荐
- Android 如何进行页面传递对象
当我们从一个页面调到另一个页面的时候,需要把该页面的一些设定值也传递给下一个页面.当要传递的值很多时,我们可以传递一个对象. 页面1: Intent intent = new Intent(PageO ...
- SpringBoot中关于Mybatis使用的三个问题
SpringBoot中关于Mybatis使用的三个问题 转载请注明源地址:http://www.cnblogs.com/funnyzpc/p/8495453.html 原本是要讲讲PostgreSQL ...
- 浅谈CDN、SEO、XSS、CSRF
CDN 什么是CDN 初学Web开发的时候,多多少少都会听过这个名词->CDN. CDN在我没接触之前,它给我的印象是用来优化网络请求的,我第一次用到CDN的时候是在找JS文件时.当时找不到相对 ...
- scrapy回调函数传递参数
scrapy.Request 的callback传参的两种方式 1.使用 lambda方式传递参数 def parse(self, response): for sel in response.xpa ...
- SecureCRT8.0设置语法高亮
SecureCRT默认不显示语法高亮,整个界面颜色单一,用起来很不舒服,也没有效率,所有通过设置一下语法高亮还是很有必要的, 默认字体也看着不是很清晰.所以还是修改一下预告高亮比较好 设置语法高亮,多 ...
- Android设置View抖动动画
在应用中,有时候我们要吸引用户去点击某些按钮,比如应用市场的推荐按钮,为了能够吸引用户主动点击而且不过分的打扰用户,最好的方法就是给我们想吸引用户注意的view添加一些抖动动画,比如这张图 这里我主要 ...
- ffmpeg tutorial01 再分析
如下图
- Ubutu Chrome 出现adobe flash is out of date的解决方法
我们需要到官网下载flash player,网址:https://get.adobe.com/flashplayer/ 不过这里要说明一下: 一般的浏览器使用的是npapi,即adobe flash ...
- dojo省份地市级联之地市封装类(二)
dojo省份地市级联之地市封装类 City.java: /** * 地市封装类 */ package com.you.model; import java.io.Serializable; /** * ...
- 程序bug致损失400亿,判程序员坐牢? 搞笑我们是认真的
号外!号外!走过,路过,不要错过!日本 IT 业的狗血八卦继续独家放送啦!! 2015 年 9 月 3 日,随着东京最高法院驳回瑞穗证券的上诉,维持二审的原判结果,一个长达 10 年的诉讼终于画下了句 ...