在setTimeout上更改匿名函数的范围会导致奇怪的警告

Dim*_*off 3 javascript scope

这让我感兴趣的纯粹是研究和个人发展.我有一组命名空间的函数/变量.

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

        }
    };
};
Run Code Online (Sandbox Code Playgroud)

.apply(this)DOES更改范围,因为console.log输出正确的对象,但它会立即运行该函数,然后在回调保持为空时出现异常/警告:

useless setTimeout call (missing quotes around argument?)
Run Code Online (Sandbox Code Playgroud)

这样做有一种优雅的方式吗?我知道我能做到

var _this = this;
Run Code Online (Sandbox Code Playgroud)

_this在anon回调中引用.例如,在mootools我会用.bind(this)...

不,因为这涉及动画,我不想" "在字符串周围使用,因为它需要进行评估并会影响性能......

bob*_*nce 9

for(var i = 0; i < al; i++) {
    setTimeout(function() {
        this.scrollAd(this.ads[i]);
    }.apply(this), this.timeDelay * 1000);
}
Run Code Online (Sandbox Code Playgroud)

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);
}
Run Code Online (Sandbox Code Playgroud)

但是,您可能更喜欢使用新的ECMAScript第五版功能绑定功能,该功能具有更紧凑的语法:

for (var i= 0; i<al; i++)
    setTimeout(this.scrollAd.bind(this, this.ads[i]), this.timeDelay*1000);
Run Code Online (Sandbox Code Playgroud)

(有一个function.bind针对浏览器的实现,在本答案的底部没有它本身.)