let用来处理循环事件绑定问题的原理分析

来源:互联网 发布:淘宝 唱片 编辑:程序博客网 时间:2024/06/06 20:00

      在编写js代码时,有一个问题基本上人人都会遇到,那就是用for循环为一组DOM对象绑定事件响应回调,每个事件回掉函数中i的值都是DOMlist.length。

上代码:

 var btns = document.getElementsByTagName('button');    for(var i = 0;i<btns.length;i++){            btns[i].onclick = function () {                alert(i);            }    }

      如果为3个按钮绑定事件,不管点击哪个都会弹出 3,这显然是不符合我们的需求的

      具体的原因我们下面详叙,先说两种我们经常用到的解决方法:

       1.为每个DOM对象添加一个index属性,记录当前的i值

for(var i = 0;i<btns.length;i++){            btns[i].index = i;            btns[i].onclick = function () {                alert(this.index);            }    }

      2.利用一个自掉用函数将i转换成函数内部变量,再用闭包的原理将内部变量i保存到事件回调函数执行

var btns = document.getElementsByTagName('button');    for(var i = 0;i<btns.length;i++){        (function (i) {            btns[i].onclick = function () {                alert(i);            }        })(i)    }

      以上这两种方法是我们经常应用的,但说实话这两种方法都需要一个逻辑和经验,我们都知道,语言的发展的目标和动力就是为了让编程更简单。ES版本的更新就很好的贯彻了这一理念,那让我们看看ES6中有没有能帮助我们更好解决类似问题的新特性呢。
      当然有,不然这篇文章就不会起这个名字了,直接上代码

 var btns = document.getElementsByTagName('button');    for(let i = 0;i<btns.length;i++){            btns[i].onclick = function () {                alert(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,向上找,此时就不是直接去全局找了,而是被它所在的块级作用域截胡了。