Ich versuche, setTimeout mit einer Lambda-Funktion in einer for-Schleife zu verwenden, aber es werden nur die letzten Parameter für den Inhalt der Lambda-Funktion aus der letzten Iteration der for-Schleife erfasst. In C # können wir eine neue Variable erstellen, die jedes Mal als Parameter übergeben wird, wenn sie an eine neue Lambda-Funktion übergeben wird. Dies scheint jedoch in Javascript nicht zu funktionieren. Irgendwelche Hinweise?

Die spezifische Funktion, über die ich spreche, ist setElementsByIdTimed ()

var gElems = new Array();

document.addEventListener("DOMContentLoaded", function (event) {

    //setElementsById('icon_anim_start' , "icon_anim_end");
    //setTimeout(function() {setElementsById('icon_anim_end' , "icon_anim");} , 500);

    var delay = setElementsByIdTimed('icon_anim_start' , "icon_anim_end" , 250);
    setTimeout(function() {setElementsById('icon_anim_end' , "icon_anim");} , delay);
    });

    function getElementsById (elementID){
        var elementCollection = new Array();
        var allElements = document.getElementsByTagName("*");
        for(i = 0; i < allElements.length; i++){
            if(allElements[i].id == elementID)
                elementCollection.push(allElements[i]);

        }
        return elementCollection;
    }

    function setElementsById (elementID, newID) {
        var elems = new Array();
        elems = getElementsById(elementID);

        for (var i = 0; i < elems.length; i++)
        {
            elems[i].id = newID;
        }
    }


    function setElementsByIdTimed (elementID, newID , ms) {
        var elems = new Array();
        elems = getElementsById(elementID);
        console.log("elems.length: " + elems.length);
        gElems = elems;


        for (var i = 0; i < elems.length; i++) {
            var index = i
            setTimeout(function() {
                setElementId(index, newID);
            }, ms * i);
        }

        return ms * (elems.length-1);
    }

    function setElementId (index , newID) {
        console.log ("gElems.length: " + gElems.length + "  index: " + index);
        gElems[index].id = newID;
    }
})
1
naphier 9 Aug. 2015 im 06:22

3 Antworten

Beste Antwort

Dies ist ein klassisches Problem beim Schließen von JavaScript. Grundsätzlich gibt es nur eine Instanz der Indexvariablen, die außerhalb des Kontexts der Lambda-Funktion deklariert ist. Daher verwendet jede Lambda-Funktion denselben Index und wird nach Abschluss der Schleife ausgeführt, sodass der Index bei jedem Aufruf außerhalb der Grenzen zu liegen scheint.

Damit dies funktioniert, muss index einen Schließungsbereich haben:

function setElementsByIdTimed(elementID, newID , ms)
{
    var elems = new Array();
    elems = getElementsById(elementID);
    console.log("elems.length: " + elems.length);
    gElems = elems;


    for(var i = 0; i < elems.length; i++)
    {
        var index = i
        setTimeout( setClosure(index,newID), ms * i);
    }

    return ms * (elems.length-1);
}
function setClosure( index, newID ) {
    // this lambda is called after the timeout elapses
    return function() {
            setElementId(index, newID);}
}

Sie können auch einen Selbstaufruf-Streich spielen, aber es ist ein wenig nervenaufreibend:

function setElementsByIdTimed(elementID, newID , ms)
{
    var elems = new Array();
    elems = getElementsById(elementID);
    console.log("elems.length: " + elems.length);
    gElems = elems;


    for(var i = 0; i < elems.length; i++)
    {
        var index = i
        setTimeout( (function(idx,nid) {
                return function () {
                    setElementId(idx,nid);}
            })(index, newID),
            ms * i);
    }

    return ms * (elems.length-1);
}

Dies ist praktisch die gleiche Lösung, aber die erste Syntax ist wahrscheinlich viel einfacher zu verstehen.

1
Lee Jenkins 9 Aug. 2015 im 03:41

Das Problem war in

    for(var i = 0; i < elems.length; i++)
    {
        var index = i
        setTimeout(function() {
            setElementId(index, newID);}, ms * i);
    }

Index ist immer die gleiche Variable. Versuchen:

    for(var i = 0; i < elems.length; i++)
    {

        (function(index){
          setTimeout(function() {
            setElementId(index, newID);}, ms * i);
        })(i);
    }

Jedes Mal, wenn Sie auf eine Variable innerhalb eines Abschlusses in einer Schleife zugreifen müssen, müssen Sie eine Funktion verwenden, um darauf zuzugreifen. Sie können auch ein forEach verwenden, das auf das Array angewendet wird:

    elems.forEach(function(index){
        setTimeout(function() {
            setElementId(index, newID);}, ms * i);
        }
    });
0
Emilio Platzer 9 Aug. 2015 im 03:44

Anstatt von

    for(var i = 0; i < elems.length; i++)
    {
        var index = i
        setTimeout(function() {
            setElementId(index, newID);
        }, ms * i);
    }

Verwenden Sie ein IIFE - https://developer.mozilla.org/en-US/docs / Glossar / IIFE

    for(var i = 0; i < elems.length; i++)
    {
        (function(index) {
            setTimeout(function() {
                setElementId(index, newID);
            }, ms * index);
        }(i));
    }
0
Jaromanda X 9 Aug. 2015 im 03:37