写了这样一段代码:
这段代码的目的是使用循环为多个元素帮点点击事件,但是测试发现并没有按照预想的那样正确的为元素绑定事件。
后来发现回调函数中的变量 i
的颜色和其他 i
的颜色不同,鼠标悬浮在该 i
上,IDEA 提示如下:
Mutable variable is accessible from closure.
我知道这是循环和异步调用的经典问题,可以通过闭包来解决,修改代码如下:
function clickImageIcon(msgArr, options) {
for (var i = 0; i < msgArr.length; i ++) {
(function(index) {
$('.file-wrapper:eq(' + index + ')').bind('click', function () {
recognitionContent(msgArr[index]);
$('#myModal').modal(options);
});
})(i);
}
}
但是在 Stack Overflow 上有人提出用 let
代替 var
也可以解决这个问题,代码如下:
function clickImageIcon(msgArr, options) {
for (let i = 0; i < msgArr.length; i ++) {
$('.file-wrapper:eq(' + i + ')').bind('click', function () {
recognitionContent(msgArr[i]);
$('#myModal').modal(options);
});
}
}
我想知道为什么换成 let
也可以解决问题,求各位大神告知!
1
tortorse 2018-03-18 21:43:40 +08:00
作用域、闭包、变量提升了解一下
|
2
murmur 2018-03-18 21:47:49 +08:00
给你个解决方法 当你搞不懂闭包和作用域的时候 就在循环的时候用 bind 把 i 绑死 这样循环到几他调用的时候就是几
或者更无耻一点 写个 data-index 添加到你的元素上 click 事件的时候直接读 data-index 就可以了 这两个方法省事还不纠结作用域 第一个 es5 就可以 第二个甚至连 es5 都不要求 |
3
DOLLOR 2018-03-18 21:52:53 +08:00 1
var 是函数级作用域,let 是块级作用域。
而 C 语言、JAVA 语言跟 let 关键字一样,都是块级作用域。 |
4
polythene 2018-03-18 21:56:50 +08:00
let 语句使得在每一次循环的都生成一个 i
|
6
drackzy 2018-03-18 22:04:18 +08:00
你可以看看《 You Don't know JS 》这本书闭包那章这个问题讲的很清楚
|
7
lsvih 2018-03-18 22:12:18 +08:00
```
for (var i = 0; i < msgArr.length; i ++) ``` 其实相当于 ``` var i; for (i = 0; i < msgArr.length; i ++) ``` 你 bind 的 function 拿到的是 i 的引用,也就是循环最后 i 的值。 let 从语法上说是块级作用域,在每次 for 循环中 bind 中 function 拿到的其实都是不同的 i 的引用。实际实施起来(比如用 babel 转一下)和你的立即执行函数的写法应该是差不多的,相当于传的是实参 |
8
Mojy 2018-03-18 22:25:53 +08:00
顺便学习了,let 是块级的,var 是函数级的。也就是说 let 在整个 for 循环里都是有效的,但 var 由于闭包的原因,在回掉函数里就会失效。
不知道理解的是否正确 |
9
murmur 2018-03-18 22:32:12 +08:00
@Mojy 如果我语文没问题,你是不是理解反了还是把例子看反了
var 最开始的例子是因为 i 实际上提前到循环外面来了 这样最后事件回调里引用就是 i 最后一个值 let 的块级。。算了找个语文好的大佬给你解释下吧 |
10
we2ex 2018-03-18 23:02:28 +08:00 via Android
完全抛弃 var 就好了,从 ES6 就应该全部改用 let / const 了
|
11
coolcoffee 2018-03-18 23:09:12 +08:00
看一下 babel 转换后的代码就知道 let 的工作原理了。
``` javascript "use strict"; function clickImageIcon(msgArr, options) { var _loop = function _loop(i) { $(".file-wrapper:eq(" + i + ")").bind("click", function() { recognitionContent(msgArr[i]); $("#myModal").modal(options); }); }; for (var i = 0; i < msgArr.length; i++) { _loop(i); } } ``` |
12
MinonHeart 2018-03-18 23:29:01 +08:00 via iPhone
jQuery 方案
.bind(‘ click ’, i, function (e) { recongnitionContent(msgArr[e.data]) ... }) |
13
sunjourney 2018-03-19 01:19:51 +08:00
我的天,你这代码写的太恶心了吧
``` function clickImageIcon(msgArr, options) { const $modal = $('#myModal') $('.file-wrapper').each(($el, index) => { // 如果你会事件代理,这里还可以继续优化 $el.on('click', function() { recognitionContent(msgArr[index]); // 不知道这是想干嘛,尽量不要用有副作用的函数 $modal.modal(options) }) }) clickImageIcon = null } ``` |
14
hoythan 2018-03-19 02:29:25 +08:00
JQ 事件绑定的第二个参数不是子元素吗?为啥不用。。。
|
15
viewsing 2018-03-19 08:35:59 +08:00 via Android
let 在 for 循环的声明中有有特殊的效果,每一轮都会创建一个新的块级作用域
|
16
heyOhayo 2018-03-19 17:18:18 +08:00
一看就是后端写的代码,前端不会问这么幼齿的问题~
|
17
KURANADO OP 感谢各位大佬的回答!
|