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闭包的理解的更多相关文章

  1. JS给元素循环添加事件的问题

    <ul> <li>男</li> <li>女</li> <li>老</li> <li>少</li&g ...

  2. JS闭包的理解及常见应用场景

    JS闭包的理解及常见应用场景 一.总结 一句话总结: 闭包是指有权访问另一个函数作用域中的变量的函数 1.如何从外部读取函数内部的变量,为什么? 闭包:f2可以读取f1中的变量,只要把f2作为返回值, ...

  3. js循环添加事件的问题

    1.需求 给下面每个按钮增加事件 <ul id="list"> <li>按钮1</li> <li>按钮2</li> &l ...

  4. 个人对js闭包的理解

      闭包算是前端面试的基础题,但我看了很多关于闭包的文章博客,但感觉很多对于闭包的理想还是有分歧的,现在网上对闭包的理解一般是两种: 有些文章认为闭包必须要返回嵌套函数中里面用到外面函数局部变量的方法 ...

  5. 【闭包】JS闭包深入理解

    先看题目代码: 1 2 3 4 5 6 7 8 9 10 11 12 function fun(n,o) {  console.log(o)  return {   fun:function(m){ ...

  6. js循环添加事件

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  7. JS闭包机制实现为DOM元素循环添加事件

    HTML代码: <button type='button' class='btn' id='1'>按钮1</button> <button type='button' c ...

  8. 谈一谈你对js线程的理解

    js线程:javascript是单线程的,所有任务都需要排队,这些任务分为同步任务和异步任务,单线程上有一个主线程任务.同步任务必须再主线程上排队进行,而异步任务(类似于点击事件)必须在主线程上的任务 ...

  9. 浅谈对Js闭包的理解

    理解Js的闭包,首先让我们先看几个概念 执行环境(executive environment)每个函数都有自己的执行环境,匿名函数默认为全局环境. 作用域链(scope chain)子函数继承父函数, ...

随机推荐

  1. JS中的Undefined和Null的区别

    Undefined ①在声明变量时,如果没有给变量赋值,则这个变量就是undefined类型: ②访问未声明的变量会报错误消息,但这样的变量使用 typeof 测试,返回的值为Undefined. 即 ...

  2. nyoj234 吃土豆 01背包

    思路:假设我们先只考虑一行,规则就是取了i处的土豆,每一个土豆有两种选择,拿与不拿,那么i-1和i+1处的土豆都不能再取,那么要求某一行的最大取值就用一次动态规划即可,dp(i)表示前i个土豆能取得的 ...

  3. 吾八哥学Selenium(三):操作复选框checkbox/单选框radio的方法

    复选框checkbox和单选框radio是web网站里经常会使用到的两个控件,那么在web自动化测试的时候如何利用Selenium来操作这俩控件呢?今天我们就来简单入门练习一下! html测试页面代码 ...

  4. TCP/IP读书笔记(4) IPv4和IPv6 路由选择

    TCP/IP读书笔记(4) IPv4和IPv6 路由选择 网络层是位于链路层之上,TCP/IP模型中网络层的核心协议是IP协议(Internet protocol). 目前主流的IP协议是IPv4(I ...

  5. JVM笔记5-对象的访问定位。

    java虚拟机中指定一个栈内存的引用指向了堆内存中的对象.这样说只是笼统的说法.而指向堆内存中的对象就一定是栈引用所需要的那个对象吗?其实并不定. 这就需要知道对象的访问定位方式有两种: 1.使用句柄 ...

  6. vue之render基本书写方法

    Vue 推荐在绝大多数情况下使用 template 来创建你的 HTML.然而在一些场景中,你真的需要 JavaScript 的完全编程的能力,这就是 render 函数,它比 template 更接 ...

  7. 一句Python,一句R︱pandas模块——高级版data.frame

    先学了R,最近刚刚上手python,所以想着将python和R结合起来互相对比来更好理解python.最好就是一句python,对应写一句R. pandas可谓如雷贯耳,数据处理神器. 以下符号: = ...

  8. dojo省份地市级联之省份Dao实现类(五)

    dojo省份地市级联之省份Dao实现类 ProvinceDaoImpl.java: /** * */ package com.you.dao.impl; import java.util.ArrayL ...

  9. WPF自学入门(一)WPF-XAML基本知识

    一.基本概念 1.XAML是派生自XML的可扩展应用程序标记语言(Extensible Application Markup Language)由微软创造应用在WPF,Silverlight等开发技术 ...

  10. UEFI模式 Thinkpad t470p Ubuntu 16.04 LTS

    准备阶段 使用官方推荐的Rufus制作U盘启动盘 在Windows 10系统下压缩出来一些空间(60G),不要分配盘符 系统设置 在Bios中关闭secure boot (设置为Disenabled) ...