在编写js代码时,有一个问题基本上人人都会遇到,那就是用for循环为一组DOM对象绑定事件响应回调,每个事件回掉函数中i的值都是DOMlist.length。
上代码:
var btns = document.getElementsByTagName('button'); for(var i = 0;i如果为3个按钮绑定事件,不管点击哪个都会弹出 3,这显然是不符合我们的需求的 具体的原因我们下面详叙,先说两种我们经常用到的解决方法:
1.为每个DOM对象添加一个index属性,记录当前的i值
for(var i = 0;i2.利用一个自掉用函数将i转换成函数内部变量,再用闭包的原理将内部变量i保存到事件回调函数执行
var btns = document.getElementsByTagName('button'); for(var i = 0;i以上这两种方法是我们经常应用的,但说实话这两种方法都需要一个逻辑和经验,我们都知道,语言的发展的目标和动力就是为了让编程更简单。ES版本的更新就很好的贯彻了这一理念,那让我们看看ES6中有没有能帮助我们更好解决类似问题的新特性呢。 当然有,不然这篇文章就不会起这个名字了,直接上代码
var btns = document.getElementsByTagName('button'); for(let i = 0;i没错我们只需要把for循环中的var改成let即可。 大家很多都用过或者听说过这种方法,但这种解决方式的原理又是什么呢?这个问题的探究可以让我们更好的理解let的特性和事件响应函数的执行机理。下面就和大家一起探讨一下原因:
*首先,我们先要解释我们为什么会遇到最初的那个问题
那个问题出现的条件是js的三个语言特性(1)js没有块级作用域 (2)事件响应函数通常在非回调函数解析完后执行(这涉及到js引擎解析代码的机制和事件轮询机制)。(3)变量的访问是从当前作用域开始,顺着作用域链向上查找的。这个两个语言特性就造成事件响应函数执行时的i的获取的是全局的(也可能是某个函数作用域中的),就算i是在for循环中定义的也一样。然而此时for循环早就执行完了,i也在多次被赋值后定在btn.length。
*明白了问题出现的原因,我们再来看let有的特性,然后在分析它解决问题的原理:
1.在块作用域有效 2. 在一个作用域内不能重复声明一个变量 3. 不会预处理,不存在变量提升
*此时大家应该都明白,是第一个特性的功劳
如果还没明白,那想必就是对块级作用域的概念还未理解,简单的说块级作用域就是一个{},在这里每一次进入for循环都会有一个循环体{},也就是说在这个例子中有三个相互独立的块级作用域。
let的引入,让块级作用域有了概念,所以此时我们才能说事件响应函数是在某一个块级作用域总定义的。在事件响应函数执行时要访问i,函数作用域中没有i,向上找,此时就不是直接去全局找了,而是被它所在的块级作用域截胡了。