无法在 setTimeout 内正确访问数组元素
这就是我要做的事情。 我有一个如下数组
var my_array = ['1', '2', '3' ... ,'1000000000000000'];
我想要做的是为该数组的每个元素创建一堆 HTML 元素,并且由于数组可以包含大量元素,因此我尝试执行以下操作,以便浏览器不会冻结。
for(var i in my_array)
{
if(my_array.hasOwnProperty(i))
{
setTimeout(function(){
do_something_with_data(my_array[i]);
});
}
}
但是,setTimeout 中的 my_array[i] 没有它应有的值。
更准确地说,当我尝试
console.log(my_array[i])
时,我得到的是这样的:
"getUnique" function (){
var u = {}, a = [];
for(var i = 0, l = this.length; i < l; ++i){
if(u.hasOwnProperty(this[i])) {
continue;
}
a.push(this[i]);
u[this[i]] = 1;
}
return a;
}
getUnique 是我添加到 Array 原型的一个函数,就像这个:
Array.prototype.getUnique = function(){
var u = {}, a = [];
for(var i = 0, l = this.length; i < l; ++i){
if(u.hasOwnProperty(this[i])) {
continue;
}
a.push(this[i]);
u[this[i]] = 1;
}
return a;
};
有人可以帮我解决这个问题吗?
setTimeout 在循环完成后执行,并且
i
是此时的最后一个键或一些垃圾值。您可以像这样捕获 i:
for (var i in my_array) {
if (my_array.hasOwnProperty(i)) {
(function(capturedI) {
setTimeout(function() {
do_something_with_data(my_array[capturedI]);
});
})(i);
}
}
您也不应该对数组使用
for..in
循环,因为它比 for 循环慢一个数量级(尤其是在使用
.hasOwnProperty
检查时),并且迭代顺序未定义
如果您有 jQuery 或愿意为旧版浏览器添加一些额外的代码,您可以执行以下操作:
my_array.forEach( function( item ) {
setTimeout( function() {
do_something_with_data( item );
}, 1000);
});
使用 jQuery:
$.each( my_array, function( index, item ) {
setTimeout( function() {
do_something_with_data( item );
}, 1000);
});
请参阅
[].forEach
问题是,您创建的函数对
i
变量有一个
引用
,而不是其
值
的副本,因此在运行时,它们看到的是当时的
i
(大概是超出了数组的末尾)。(更多信息:
闭包并不复杂
)
我建议采用一种完全不同的方法(见下文),但首先,让我们看看如何使您现有的方法发挥作用。
要使用
for
循环执行您要执行的操作,您必须让函数在不会改变的某些东西上闭包。通常的做法是使用工厂函数来创建超时函数,以便它们关闭工厂的参数。或者实际上,您可以传入数组元素的值而不是索引变量。
for(var i in my_array)
{
if(my_array.hasOwnProperty(i))
{
setTimeout(makeFunction(my_array[i]));
}
}
function makeFunction(entry) {
return function(){
do_something_with_data(entry);
};
}
但是 ,我可能会重新构建代码,这样您就不会不必要地创建大量的函数对象。相反,使用 一个 函数,并让它关闭它递增的索引:
// Assumes `my_array` exists at this point, and that it
// has at least one entry
var i = 0;
setTimeout(tick, 0);
function tick() {
// Process this entry
if (my_array.hasOwnProperty(i)) {
do_something_with_data(my_array[i]);
}
// Move to next
++i;
// If there are any left, schedule the next tick
if (i < my_array.length) {
setTimeout(tick, 0);
}
}
这只是猜测。尝试一下:
for(var i in my_array)
{
if(my_array.hasOwnProperty(i))
setTimeout("do_something_with_data('"+my_array[i]+"')", 500);
}