这让我感兴趣的纯粹是研究和个人发展。我有一组命名空间的函数/变量。
在1个函数中我需要通过setTimeout调用另一个函数但是将范围保持为'this'。我正在努力解决这个问题,似乎无法在setTimeout运行时绑定它。
var foo = {
ads: ["foo","bar"],
timeDelay: 3,
loadAds: function() {
var al = this.ads.length;
if (!al)
return; // no ads
for(var i = 0; i < al; i++) {
setTimeout(function() {
this.scrollAd(this.ads[i]);
}.apply(this), this.timeDelay * 1000);
}
},
scrollAd: function(adBlock) {
console.log(adBlock);
}
};
};
.apply(this)DOES更改范围,因为console.log会输出正确的对象,但它会立即运行该函数,然后在回调保持为空时出现异常/警告:
useless setTimeout call (missing quotes around argument?)
有一种优雅的方式可以做到这一点吗?我知道我能做到
var _this = this;
并在anon回调中引用_this
。例如,在mootools中我会使用.bind(this)
而不是......
并且不,因为这涉及动画,我不想在字符串周围使用" "
因为它需要被评估并且会影响性能......
答案 0 :(得分:9)
for(var i = 0; i < al; i++) {
setTimeout(function() {
this.scrollAd(this.ads[i]);
}.apply(this), this.timeDelay * 1000);
}
apply
不会绑定一个函数,它会调用它。因此,您立即执行滚动,然后将其返回值(undefined
)传递给setTimeout
,这是无效的。
你可能打算在this
和循环变量之间使用这样的闭包(必须关闭它,或者每次超时后它都是相同的,循环后的值):
for(var i = 0; i < al; i++) {
setTimeout(function(that, j) {
return function() {
that.scrollAd(that.ads[j]);
};
}(this, i), this.timeDelay * 1000);
}
但是您可能更喜欢使用新的ECMAScript第五版功能绑定功能,该功能具有更紧凑的语法:
for (var i= 0; i<al; i++)
setTimeout(this.scrollAd.bind(this, this.ads[i]), this.timeDelay*1000);
(function.bind
的实施方案适用于this answer底部没有本地浏览器的浏览器。)
答案 1 :(得分:2)
据我所知,你确实应该使用这样的东西:
var self = this;
setTimeout(function(){self.scrollAd(ad);}, this.timeDelay * 1000);
但如果您非常想使用.apply()
,那就这样做:
var self = this;
setTimeout(function(){
function(){
}.apply(self);
}, this.timeDelay * 1000);
另请注意,如果在for
循环内运行此函数并在计时器中运行的函数内使用i
的值,则函数将始终以{{的最后一个值运行1}}(即i
)。为了解决这个问题,您需要分别使用i == al
的每个值进行闭包。
所以拿你的代码并让它工作它应该是这样的:
i
注意:我没有运行此代码,因此可能包含一些错误。
如果我是你,我会使用来自对象的数据,而不是将其传递给var foo = {
ads: ["foo","bar"],
timeDelay: 3,
loadAds: function() {
function runTimed(o, fn, args, time)
{
setTimeout(function(){ fn.apply(o, args); }, time);
}
var al = this.ads.length;
if (!al)
return; // no ads
for(var i = 0; i < al; i++) {
runTimed(this, this.scrollAd, this.ads[i], this.timeDelay*1000);
}
},
scrollAd: function(adBlock) {
console.log(adBlock);
}
};
};
(scrollAd
就足够了。)