与 lambda 函数参数混淆

Confusion with lambda function parameter

我试图在 for 循环中将 setTimeout 与 lambda 函数一起使用,但它仅从 for 循环的最后一次迭代中捕获 lambda 函数内容的最后一个参数。在 C# 中,我们可以创建一个新变量,每次将其传递给新的 lambda 函数时作为参数传递,但这在 javascript 中似乎不起作用。有什么线索吗?

我说的具体函数是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;
    }
})

而不是

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

使用 IIFE - https://developer.mozilla.org/en-US/docs/Glossary/IIFE

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

问题出在

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

索引总是同一个变量。尝试:

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

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

每次您必须在循环中访问一个闭包中的变量时,您必须使用一个函数来访问它。您还可以使用应用于数组的 forEach:

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

这是一个经典的 JavaScript 闭包问题。基本上,索引变量只有一个实例,并且它是在 lambda 函数的上下文之外声明的。所以每个 lambda 函数都使用相同的索引,并且它们在循环完成后执行,因此索引在每次调用时看起来都是越界的。

要让它工作 index 必须有闭包作用域:

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);}
}

你也可以玩一个自我调用的把戏,但有点费脑子:

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);
}

这些实际上是相同的解决方案,但第一种语法可能更容易掌握。