Javascript Closure Problem

2019-03-04 11:40发布

问题:

I know this kind of question gets asked alot, but I still haven't been able to find a way to make this work correctly.

The code:

function doStuff () {
     for (var i = 0; i< elementsList.length; i++) {
        elementsList[i].previousSibling.lastChild.addEventListener("click", function(){
                   toggle(elementsList[i])}, false);


        }
    } // ends function


    function toggle (element) {
        alert (element);
    }

The problem is in passing variables to the toggle function. It works with the this keyword (but that sends a reference to the clicked item, which in this case is useless), but not with elementsList[i] which alerts as undefined in Firefox.

As I understood it, using anonymous functions to call a function is enough to deal with closure problems, so what have I missed?

回答1:

Try:

function startOfFunction() {
    for (var i = 0; i< elementsList.length; i++) {
        elementsList[i].previousSibling.lastChild.addEventListener(
            "click",
            (function(el){return function(){toggle(el);};})(elementsList[i]),
            false
        );

    }
} // ends function


function toggle (element) {
    alert (element);
}


回答2:

The Problem is, that you want to use the var i! i is available in the onClick Event, (since closure and stuff). Since you have a loop, i is counted up. Now, if you click on any of the elements, i will always be elementsList.length (since all event functions access the same i )!

using the solution of Matt will work.



回答3:

As an explanation: the anonymous function you use in the for loop references the variable "i" to get the element to toggle. As anonymous functions use the "live" value of the variable, when somebody clicks the element, "i" will always be elementsList.length+1.

The code example from Matt solves this by sticking the i into another function in which it is "fixated". This always holds true:

If you iterate over elements attaching events, do not use simple anonymous functions as they screw up, but rather create a new function for each element. The more readable version of Matts answer would be:

function iterate () {
    for (var i = 0; i < list.length; i++) {
        // In here, i changes, so list[i] changes all the time, too. Pass it on!
        list[i].addEventListener(createEventFunction(list[i]);
    }
 }

 function createEventFunction (item) {
     // In here, item is fixed as it is passed as a function parameter.
     return function (event) {
         alert(item);
     };
 }


回答4:

Try:

function doStuff () {
    for (var i = 0; i< elementsList.length; i++) {
        (function(x) {
            elementsList[x].previousSibling.lastChild.addEventListener("click", function(){
               toggle(elementsList[x])}, false);
        })(i);



    }
} // ends function

I think it might be an issue with passing elementsList[i] around, so the above code has a closure which should help.