计时器在关闭

时间:2013-08-25 18:02:55

标签: javascript closures

任何人都可以告诉我为什么这不起作用吗?

(function() {
    window.Test = {};
})();

Test.Timers = {
    c: null,
    startTimer: function() { c = 0; setTimeout(this.doWork, 0); },
    doWork: function() {
        c++;
        alert(c);
        setTimeout(this.doWork, 0);
    }
};

当我调用Test.Timers.startTimer()时,它只用1警告一次。 感谢

2 个答案:

答案 0 :(得分:1)

方法不会“记住”其所有者(其this);您可以将方法从一个对象复制到另一个对象,并将其视为任何其他函数。当您使用点符号实际调用它时,它只有正确的所有者,例如this.doWork()

所以你的问题是你将函数this.doWork传递给setTimeout,然后在不知道其所有者的情况下将其作为函数调用,突然它this为{{{ 1}}而不是你的计时器对象。要解决此问题,您需要自己跟踪window。例如,您可以写:

this

或:

Test.Timers = (function () {
    var newTimer = {
        c: null,
        startTimer: function() {
            this.c = 0;
            setTimeout(function () { newTimer.doWork(); }, 0);
        },
        doWork: function() {
            this.c++;
            alert(this.c);
            setTimeout(function () { newTimer.doWork(); }, 0);
        }
    };
    return newTimer;
 })();

(请注意,我还在必要时将Test.Timers = (function () { var startTimer = function() { newTimer.c = 0; setTimeout(doWork, 0); }; var doWork = function() { newTimer.c++; alert(newTimer.c); setTimeout(doWork, 0); }; var newTimer = { c: null, startTimer: startTimer, doWork: doWork }; return newTimer; })(); 更改为cthis.c,因为您的版本重复提到newTimer.c。另请注意,在第二个版本中,如果您不需要外部代码就可以访问window.c,您可以将其更改为本地变量,使事情更清晰。)

答案 1 :(得分:0)

根据您对Ruakh答案的评论,我更喜欢以下方法,我自己:

Test.Timers = (function () {
    var this_ = {
        c: null,
        startTimer: function() { this_.c = 0; setTimeout(this_.doWork, 0); },
        doWork: function() {
            this_.c++;
            alert(this_.c);
            setTimeout(this_.doWork, 0);
        }
    };
    return this_;
})();

这样,意思很清楚,因为this_看起来像this,而你所要做的就是习惯于制作匿名函数并立即调用它的闭包模式。另请注意,我修改了对c的引用,以引用this_.c而不是全局变量c

或者,您可以使用.bind()将函数的this绑定到特定的东西。这至少是Chrome的V8内置,也可能是Firefox:

Test.Timers = {
    c: null,
    startTimer: function() { this.c = 0; setTimeout(this.doWork, 0); },
    doWork: function() {
        this.c++;
        alert(this.c);
        setTimeout(this.doWork, 0);
    }
};
Test.Timers.startTimer = Test.Timers.startTimer.bind(Test.Timers);
Test.Timers.doWork = Test.Timers.doWork.bind(Test.Timers);