从循环添加事件谈起对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)子函数继承父函数, ...
随机推荐
- MysqL读写分离的实现-Mysql proxy中间件的使用
为什么要架设读写分离,这里不做多余的说明,想了解具体原理,请百度或者参考其他帖子.在这里只做大概的配置说明,测试中使用三台服务器 192.168.136.142 主服务器 192.168.136. ...
- Docker镜像的构成__Dockerfile
相比docker commit来说,Dockerfile的方法会更加自动化,更加方便快捷,而且功能也更强大.拿构建Nginx容器举例. 先创建一个目录 mkdir /opt/docker-file 进 ...
- 老男孩Python全栈开发(92天全)视频教程 自学笔记20
day20课程内容: 模块: #模块一共三种:1.Python标准库 2.第三方模块 3.应用程序自定义的模块#import sysimport jisuanapp#print(jisuanapp.a ...
- hihoCoder1330 数组重排
题意 小Hi想知道,如果他每次都按照一种固定的顺序重排数组,那么最少经过几次重排之后数组会恢复初始的顺序? 具体来讲,给定一个1 - N 的排列 P,小Hi每次重排都是把第 i 个元素放到第 Pi个位 ...
- UVA1600 状态BFS
刚开是我用了一种很笨的bfs过掉的,后来看到原来还可以三维带状态BFS,觉得是一个不错的思路. d[x][y][k]表示坐标位于(x,y)经过K个障碍到达时的最短路径,当然如果(x,y)处的数字是0就 ...
- linux dns搭建
DNS:域名解析(Domain Nmae System)正向解析:根据主机名称(域名)查找其对应的ip地址,这是最基本,最常用的功能反向解析:根据ip地址查找其对应的主机名称(域名),反垃圾邮件/安全 ...
- CSS3之Border-radius
1.属性介绍 border-radius:none|12.3px,取值不可为负数,表示边框圆角 相关属性:border-top-right-radius , border-bottom-right-r ...
- SQL面试题收录
1.用一条SQL语句 查询出每门课都大于80分的学生姓名 SELECT DISTINCT NAME FROM t_score_tab WHERE NAME NOT IN (SELECT DISTINC ...
- C# GUID介绍和的使用
GUID(全局统一标识符)是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一的.通常平台会提供生成GUID的API.生成算法很有意思,用到了以太网卡地址.纳秒级时间.芯片ID码和许多可 ...
- TypeError: Error #1034: 强制转换类型失败:无法将 mx.controls::DataGrid@9a7c0a1 转换为 spark.core.IViewport。
1.错误描述 TypeError: Error #1034: 强制转换类型失败:无法将 mx.controls::DataGrid@9aa90a1 转换为 spark.core.IViewport. ...