JavaScript let 关键字问题求解

2018-03-18 21:40:04 +08:00
 KURANADO

写了这样一段代码:

这段代码的目的是使用循环为多个元素帮点点击事件,但是测试发现并没有按照预想的那样正确的为元素绑定事件。

后来发现回调函数中的变量 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 也可以解决问题,求各位大神告知!

4894 次点击
所在节点    JavaScript
18 条回复
tortorse
2018-03-18 21:43:40 +08:00
作用域、闭包、变量提升了解一下
murmur
2018-03-18 21:47:49 +08:00
给你个解决方法 当你搞不懂闭包和作用域的时候 就在循环的时候用 bind 把 i 绑死 这样循环到几他调用的时候就是几
或者更无耻一点 写个 data-index 添加到你的元素上 click 事件的时候直接读 data-index 就可以了
这两个方法省事还不纠结作用域 第一个 es5 就可以 第二个甚至连 es5 都不要求
DOLLOR
2018-03-18 21:52:53 +08:00
var 是函数级作用域,let 是块级作用域。
而 C 语言、JAVA 语言跟 let 关键字一样,都是块级作用域。
polythene
2018-03-18 21:56:50 +08:00
let 语句使得在每一次循环的都生成一个 i
murmur
2018-03-18 22:01:06 +08:00
@polythene babel 转义不是只处理变量名冲突的时候才会把 i 换成_i 么
那这个在 babel 转义后还有用么
drackzy
2018-03-18 22:04:18 +08:00
你可以看看《 You Don't know JS 》这本书闭包那章这个问题讲的很清楚
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 转一下)和你的立即执行函数的写法应该是差不多的,相当于传的是实参
Mojy
2018-03-18 22:25:53 +08:00
顺便学习了,let 是块级的,var 是函数级的。也就是说 let 在整个 for 循环里都是有效的,但 var 由于闭包的原因,在回掉函数里就会失效。
不知道理解的是否正确
murmur
2018-03-18 22:32:12 +08:00
@Mojy 如果我语文没问题,你是不是理解反了还是把例子看反了
var 最开始的例子是因为 i 实际上提前到循环外面来了 这样最后事件回调里引用就是 i 最后一个值
let 的块级。。算了找个语文好的大佬给你解释下吧
we2ex
2018-03-18 23:02:28 +08:00
完全抛弃 var 就好了,从 ES6 就应该全部改用 let / const 了
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);
}
}


```
MinonHeart
2018-03-18 23:29:01 +08:00
jQuery 方案
.bind(‘ click ’, i, function (e) {
recongnitionContent(msgArr[e.data])
...
})
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
}
```
hoythan
2018-03-19 02:29:25 +08:00
JQ 事件绑定的第二个参数不是子元素吗?为啥不用。。。
viewsing
2018-03-19 08:35:59 +08:00
let 在 for 循环的声明中有有特殊的效果,每一轮都会创建一个新的块级作用域
heyOhayo
2018-03-19 17:18:18 +08:00
一看就是后端写的代码,前端不会问这么幼齿的问题~
KURANADO
2018-03-20 16:01:22 +08:00
感谢各位大佬的回答!
pheyer
2018-03-21 15:07:11 +08:00
@lsvih 你这个等效是不是有点不对,把 var 换成 let,i 变量的声明仍然在 for 循环之前的

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/439191

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX