Javascript let 关键字问题求解 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐关注
Meteor
JSLint - a Javascript code quality tool
jsFiddle
D3.js
WebStorm
推荐书目
Javascript 权威指南第 5 版
Closure: The Definitive Guide
KURANADO
V2EX    Javascript

Javascript let 关键字问题求解

  •  
  •   KURANADO 2018-03-18 21:40:04 +08:00 5311 次点击
    这是一个创建于 2768 天前的主题,其中的信息可能已经有所发展或是发生改变。

    写了这样一段代码:

    mark

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

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

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


    ```
    MinonHeart
        12
    MinonHeart  
       2018-03-18 23:29:01 +08:00 via iPhone
    jQuery 方案
    .bind(‘ click ’, i, function (e) {
    recongnitionContent(msgArr[e.data])
    ...
    })
    sunjourney
        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
    }
    ```
    hoythan
        14
    hoythan  
       2018-03-19 02:29:25 +08:00
    JQ 事件绑定的第二个参数不是子元素吗?为啥不用。。。
    viewsing
        15
    viewsing  
       2018-03-19 08:35:59 +08:00 via Android
    let 在 for 循环的声明中有有特殊的效果,每一轮都会创建一个新的块级作用域
    heyOhayo
        16
    heyOhayo  
       2018-03-19 17:18:18 +08:00
    一看就是后端写的代码,前端不会问这么幼齿的问题~
    KURANADO
        17
    KURANADO  
    OP
       2018-03-20 16:01:22 +08:00
    感谢各位大佬的回答!
    pheyer
        18
    pheyer  
       2018-03-21 15:07:11 +08:00
    @lsvih 你这个等效是不是有点不对,把 var 换成 let,i 变量的声明仍然在 for 循环之前的
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     4032 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 22ms UTC 04:10 PVG 12:10 LAX 21:10 JFK 00:10
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86