Mutable variable is accessible from closure. How can I fix this?

前端 未结 5 1910
没有蜡笔的小新
没有蜡笔的小新 2020-12-12 12:17

I am using Typeahead by twitter. I am running into this warning from Intellij. This is causing the \"window.location.href\" for each link to be the last item in my list of i

相关标签:
5条回答
  • 2020-12-12 12:32

    I liked the paragraph Closures Inside Loops from Javascript Garden

    It explains three ways of doing it.

    The wrong way of using a closure inside a loop

    for(var i = 0; i < 10; i++) {
        setTimeout(function() {
            console.log(i);  
        }, 1000);
    }
    

    Solution 1 with anonymous wrapper

    for(var i = 0; i < 10; i++) {
        (function(e) {
            setTimeout(function() {
                console.log(e);  
            }, 1000);
        })(i);
    }
    

    Solution 2 - returning a function from a closure

    for(var i = 0; i < 10; i++) {
        setTimeout((function(e) {
            return function() {
                console.log(e);
            }
        })(i), 1000)
    }
    

    Solution 3, my favorite, where I think I finally understood bind - yaay! bind FTW!

    for(var i = 0; i < 10; i++) {
        setTimeout(console.log.bind(console, i), 1000);
    }
    

    I highly recommend Javascript garden - it showed me this and many more Javascript quirks (and made me like JS even more).

    p.s. if your brain didn't melt you haven't had enough Javascript that day.

    0 讨论(0)
  • 2020-12-12 12:35

    Since the only scoping that JavaScript has is function scope, you can simply move the closure to an external function, outside of the scope you're in.

    0 讨论(0)
  • 2020-12-12 12:37

    You need to nest two functions here, creating a new closure that captures the value of the variable (instead of the variable itself) at the moment the closure is created. You can do this using arguments to an immediately-invoked outer function. Replace this expression:

    function (item) { // what to do when item is selected
        comp = me.map[item];
        if (typeof comp === 'undefined') {
            return this.query;
        }
    
        window.location.href = me.format(gotoUrl, comp.s, target.destination);
    
        return item;
    }
    

    With this:

    (function (inner_target) {
        return function (item) { // what to do when item is selected
            comp = me.map[item];
            if (typeof comp === 'undefined') {
                return this.query;
            }
    
            window.location.href = me.format(gotoUrl, comp.s, inner_target.destination);
    
            return item;
        }
    }(target))
    

    Note that we pass target into the outer function, which becomes the argument inner_target, effectively capturing the value of target at the moment the outer function is called. The outer function returns an inner function, which uses inner_target instead of target, and inner_target will not change.

    (Note that you can rename inner_target to target and you will be okay -- the closest target will be used, which would be the function parameter. However, having two variables with the same name in such a tight scope could be very confusing and so I have named them differently in my example so that you can see what's going on.)

    0 讨论(0)
  • 2020-12-12 12:43

    Just to clarify on @BogdanRuzhitskiy answer (as I couldn't figure out how to add the code in a comment), the idea with using let is to create a local variable inside the for block:

    for(var i = 0; i < 10; i++) {
        let captureI = i;
        setTimeout(function() {
           console.log(captureI);  
        }, 1000);
    }
    

    This will work in pretty much any modern browser except IE11.

    0 讨论(0)
  • 2020-12-12 12:47

    In ecmascript 6 we have new opportunities.

    The let statement declares a block scope local variable, optionally initializing it to a value. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let

    0 讨论(0)
提交回复
热议问题