我正在尝试在for循环中使用带有lambda函数的setTimeout,但它只从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;
}
})
答案 0 :(得分:1)
这是一个典型的JavaScript闭包问题。基本上,只有一个索引变量实例,并且它在lambda函数的上下文之外声明。所以每个lambda函数都使用相同的索引,并且它们在循环完成后执行,因此index在每次调用时看起来都是越界的。
要使其正常工作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);
}
这些解决方案实际上是相同的,但第一种语法可能更容易理解。
答案 1 :(得分:0)
而不是
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));
}
答案 2 :(得分:0)
问题在于
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);
}
每次必须在循环中访问clousure中的变量时,必须使用函数来访问它。您还可以使用forEach应用于数组:
elems.forEach(function(index){
setTimeout(function() {
setElementId(index, newID);}, ms * i);
}
});