从循环添加事件谈起对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)子函数继承父函数, ...
随机推荐
- 买帽子 (hash)
思路:表示数字i出现的次数,在输入的同时记录每个数字出现的次数.最后从0枚举到1000判断第三个是否存在,存在则记录该数字. #include <stdio.h> #include < ...
- django框架 - 环境的搭建
本章介绍django的mac环境的搭建,IDE使用的是pycharm,步骤如下: 第一步:新建项目 第二步:选择django项目模版 第三步:在终端中使用命令创建myapp应用 第四步:在settin ...
- 6.3 x86处理器如何处理MSI-X中断请求
PCIe设备发出MSI-X中断请求的方法与发出MSI中断请求的方法类似,都是向Message Address所在的地址写Message Data字段包含的数据.只是MSI-X中断机制为了支持更多的中断 ...
- FFMPEG 实现 YUV,RGB各种图像原始数据之间的转换(swscale)
FFMPEG中的swscale提供了视频原始数据(YUV420,YUV422,YUV444,RGB24...)之间的转换,分辨率变换等操作,使用起来十分方便,在这里记录一下它的用法. swscale主 ...
- 【mongodb系统学习之十一】mongodb删除数据
十一.mongodb删除数据: 1).删除全部文档:remove,语法db.collectionName.remove({}):小括号里边必须要有条件,否则不成功:如果只是一个空的{},则会删除集合内 ...
- 硬盘GPT分区与MBR分区的转换
如何将gpt分区更改成mbr分区? "因为笔记本电脑硬盘分区表是GPT而导致大家无法安装引导系统.需要转换为MBR分区还能顺利安装." 问题是,分区工具无法转换MBR,这里小编知道 ...
- EJB相关的面试题
1.EJB是基于哪些技术实现的?并说出SessionBean和EntityBean的区别,StatefulBean和StatelessBean的区别. (1)EJB包括Session Bean.Ent ...
- 使用Python收集获取Linux系统主机信息
爬虫代理IP由芝麻HTTP服务供应商提供 使用 python 代码收集主机的系统信息,主要:主机名称.IP.系统版本.服务器厂商.型号.序列号.CPU信息.内存等系统信息. #!/usr/bin/en ...
- Python实现常用的数据结构
Python中的数据结构 #巧用Python列表特性实现特定数据结构 #栈实现stack = []stack.push(x)stack.pop()stack[-1] #队列实现from colle ...
- gplots heatmap.2和ggplot2 geom_tile实现数据聚类和热图plot
主要步骤 ggplot2 数据处理成矩阵形式,给行名列名 hclust聚类,改变矩阵行列顺序为聚类后的顺序 melt数据,处理成ggplot2能够直接处理的数据结构,并加上列名 ggplot_tile ...