从循环添加事件谈起对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)子函数继承父函数, ...
随机推荐
- Nexus3将本地jar包添加到仓库
新建一个文件夹,将要上传的jar包放进去,然后创建一个pom文件,例如xx.jar,pom.xml 首先创建一个目录 方便执行上传的时候url参数 也可以不创建, 上传XML curl -v -u a ...
- 阿里云ECS重置磁盘到SSH登录
1.登录阿里云(www.aliyun.com) -- > 控制台: 2.点击左边的"云服务器ECS": 3.点击上面"第二步",进入页面之后,点击&quo ...
- the c programing language 学习过程7
interact 互动 carriage运费运输 linefeed 换行 redirection改方向 interleaved交叉存取 adequate足够的 untouched原样的 specif ...
- nyoj281 整数中的1(二) 数位DP
和整数中的1一毛一样.就是输入时改了一下罢了. AC代码: #include<cstdio> const int maxn = 35; int w[maxn], h[maxn]; void ...
- Ironic中pxe driver和agent driver的区别
历史问题: 以pxe_ipmitool 和agent_ipmitool为例,看起来似乎前者不使用ironic-python-agent,后者使用,但是实际上两者都使用ironic-python-age ...
- Hive导入HDFS/本地数据
#创建表人信息表 person(String name,int age) hive> create table person(name STRING,age INT)ROW FORMAT DE ...
- sqoop:mysql和Hbase/Hive/Hdfs之间相互导入数据
1.安装sqoop 请参考http://www.cnblogs.com/Richardzhu/p/3322635.html 增加了SQOOP_HOME相关环境变量:source ~/.bashrc ...
- openstack-ocata-身份验证2
Identity service 一.身份服务概述 OpenStack身份管理服务提供一个单点集成身份验证.授权和目录服务. 身份服务通常是第一个服务用户与之交互.一旦身份验证,最终用户可以使用自己的 ...
- R语言︱贝叶斯网络语言实现及与朴素贝叶斯区别(笔记)
每每以为攀得众山小,可.每每又切实来到起点,大牛们,缓缓脚步来俺笔记葩分享一下吧,please~ --------------------------- 一.贝叶斯网络与朴素贝叶斯的区别 朴素贝叶斯的 ...
- 极限学习机︱R语言快速深度学习进行回归预测
本文转载于张聪的博客,链接:https://ask.hellobi.com/blog/zason/4543. 深度学习在过去几年,由于卷积神经网络的特征提取能力让这个算法又火了一下,其实在很多年以前早 ...