js 点击闭包_闭包经典使用场景之一

闭包经典使用场景之一:通过循环给页面上多个dom节点绑定事件。

题目:现在有个HTML片段,要求编写代码,点击编号为几的链接就弹出其编号

<ul>
  <li>编号1,点击我请弹出1</li>
  <li>2</li>
  <li>3</li>
  <li>4</li>
  <li>5</li>
</ul>

一般不知道这个题目用闭包的话,会写出下面的代码:

for(var i = 0; i < list.length; i++) {
  list[i].addEventListener('click', function(){
      alert(i)
    }, true)
}

或者

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

结果是点击每一项都弹出5 。原因是onclick事件是异步触发的,当事件被触发时,for循环早已结束,此时变量i的值已经是5!故当onclick事件顺着作用域链从内向外查找变量i时,找到的值总是5。

例如:https://jsbin.com/hibuxirewu/edit?html,js,output

f6aa9b29c6bf067626c1fc0e47e8f563.png

若使用闭包改写:https://jsbin.com/balofiwige/edit?html,js,output

var list = document.getElementsByTagName('li')
for(var i = 0; i < list.length; i++) {
  list[i].addEventListener('click', function(i){
    return function() {
      alert(i+1)
      console.log(i+1)
    }
  }(i), true)
}

037818bb42d675e15f044d389c54980a.png

在闭包作用下,定义事件函数的时候,每次循环的i值都封闭起来,这样在函数执行时,会查找定义时的作用域链,这个作用域链里的i值是在每次循环中都被保留的,因此点击不同的li会alert出不同编号。

下面总结onclick事件的6种解决办法:

1、加一层闭包,返回一个函数作为响应事件

https://jsbin.com/qaziqecacu/edit?html,js,output

var list = document.getElementsByTagName('li')
for(var i = 0; i < list.length; i++) {
  list[i].onclick = function(j) {
    return function() {            // 返回一个函数
      alert(j+1)
      console.log(j+1)
    }
  }(i)
}

fa56d793e134848ca9344eac72c84785.png

2、用Function实现,实际上每产生一个函数实例就会产生一个闭包

https://jsbin.com/wumeraxive/edit?html,js,output

var list = document.getElementsByTagName('li')
for(var i = 0; i < list.length; i++) {
  list[i].onclick = new Function("alert(" + i +")")
  // new一次就产生一个函数实例
}

f634c85b9723544282b544c6b775e747.png

3、用Function实现,注意与法2的区别

https://jsbin.com/dowoxiqaji/edit?html,js,output

var list = document.getElementsByTagName('li')
for(var i = 0; i < list.length; i++) {
  list[i].onclick = Function("alert(" + i +")")
}

4c2efc90aecf48bbabd9892e087f1f1d.png

4、加一层闭包,i以局部变量形式传递给内层函数

https://jsbin.com/zipakuviki/edit?html,js,output

var list = document.getElementsByTagName('li')
for(var i = 0; i < list.length; i++) {
  (function() {
    var temp = i // 调用时局部变量
    list[i].onclick = function() {
      alert(temp+1)
      console.log(temp+1)
    }
  })()
}

9511102f37390186356e8720fa8bf513.png

5、加一层闭包,i以函数参数形式传递给内层函数

https://jsbin.com/qeyequqepo/edit?html,js,output

var list = document.getElementsByTagName('li')
for(var i = 0; i < list.length; i++) {
  (function(j) {
    list[i].onclick = function() {
      alert(j+1)
      console.log(j+1)
    }
  })(i)
}

839b0c1adc453ea453b7b5b48204463a.png

6、将变量i保存在匿名函数自身

https://jsbin.com/wezayikunu/edit?html,js,output

var list = document.getElementsByTagName('li')
for(var i = 0; i < list.length; i++) {
    (list[i].onclick = function() {
      alert(arguments.callee.i + 1)
      console.log(arguments.callee.i + 1)
    }).i = i
}

611402403e02076627eba3a62f82416e.png

7、将变量保存在每个元素对象li上

https://jsbin.com/ladixutowe/edit?html,js,output

var list = document.getElementsByTagName('li')
for(var i = 0; i < list.length; i++) {
   list[i].i = i
   list[i].onclick = function() {
      alert(this.i + 1)
      console.log(this.i + 1)
   }
}

ed63d843c79b53ee5806bfe60943e9be.png

8. 先在循环之外创建一个辅助函数让其再返回一个绑定了当前i值的函数

https://jsbin.com/widetuhali/edit?html,js,output

var list = document.getElementsByTagName('li')

var helper = function(i) {
  return function() {
    alert(i+1)
    console.log(i+1)
  }
}

for(var i = 0; i < list.length; i++) {
   list[i].onclick = helper(i)
}

8bf3ce63ce50f6d647075d698a0d7bef.png
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值