不需要的Javascript效果:Prototype在实例之间共享闭包

时间:2015-02-25 22:29:54

标签: javascript closures prototype

我正在制作一个具有定时水平的游戏。水平为2分钟,倒数计时器显示在右上角。马里奥风格。

跟踪已用时间的一种方法是让我的Level对象将elapsed作为成员变量。

Level.init = function(){
    this.elapsed = 0;  //member variable!
    return this;
};

Level.update = function(){
    this.countdown();
};

Level.countdown = function(){
    this.elapsed += 1;
    var remaining = (GAME_LENGTH*60) - (this.elapsed/TICKS);
    var minutes = Math.floor(remaining/60);
    var seconds = Math.floor(remaining%60);
    this.countdown.html(minutes + ":" + ("0"+seconds).slice(-2));       

    if (this.elapsed/TICKS >= GAME_LENGTH*60) {
        this.level_end();
    }
};

当游戏引擎需要一个新的级别时,它会以这种方式创建一个:

NewLevel = Object.create(Level).init();

然而,我认为使用闭包可能会很好; elapsed仅由countdown函数使用,因此它不需要是Level的成员变量。 Level并不需要知道elapsed存在。

//member variable this.elapsed has been removed
Level.init = function(){
    return this;
};

Level.update = function(){
    this.countdown();
};

//a closure is used to keep track of elapsed time
Level.countdown = (function(){
    var elapsed = 0;        
    return function() {
        elapsed += 1;
        var remaining = (GAME_LENGTH*60) - (elapsed/TICKS);
        var minutes = Math.floor(remaining/60);
        var seconds = Math.floor(remaining%60);
        this.countdown.html(minutes + ":" + ("0"+seconds).slice(-2));       

        if (elapsed/TICKS >= GAME_LENGTH*60) {
            elapsed = 0;
            this.end_level();
        }
    };
})();

但现在我有一个不同的问题。创建的Level的所有副本共享countdown闭包,因为它位于原型链中。当用户提前退出该级别,然后启动一个新级别(废弃旧的Level对象实例化一个新的Level对象)时,闭包中的elapsed变量不会被重置。 / p>

以另一种方式说,用户提前退出等级30秒。然后用户再次开始关卡,但现在,倒数计时器仍然显示30秒,而不是整整2分钟。

有没有一种优雅的方法可以解决这个问题并仍然使用闭包?或者我必须恢复到以前的解决方案,忘记闭包,并使elapsed成为成员变量吗?

2 个答案:

答案 0 :(得分:2)

  

有没有一种优雅的方法可以解决这个问题并仍然使用闭包?或者我必须恢复到以前的解决方案,忘记闭包,并使成员变量过去了吗?

它必须是特定于实例的;这并不意味着它必须是对象的属性。您可以在init方法中创建闭包:

//member variable this.elapsed has been removed
Level.init = function(){
    //a closure is used to keep track of elapsed time
    this.countdown = createCountdownMethod();
    return this;
};

Level.update = function(){
    this.countdown();
};

function createCountdownMethod(){
    var elapsed = 0;        
    return function() {
        elapsed += 1;
        var remaining = (GAME_LENGTH*60) - (elapsed/TICKS);
        var minutes = Math.floor(remaining/60);
        var seconds = Math.floor(remaining%60);
        this.countdown.html(minutes + ":" + ("0"+seconds).slice(-2));       

        if (elapsed/TICKS >= GAME_LENGTH*60) {
            elapsed = 0;
            this.end_level();
        }
    };
}

或者这可能更容易阅读:

//member variable this.elapsed has been removed
Level.init = function(){
    var elapsed = 0;        

    //a closure is used to keep track of elapsed time
    Level.countdown = countdown;
    return this;

    function countdown() {
        elapsed += 1;
        var remaining = (GAME_LENGTH*60) - (elapsed/TICKS);
        var minutes = Math.floor(remaining/60);
        var seconds = Math.floor(remaining%60);
        this.countdown.html(minutes + ":" + ("0"+seconds).slice(-2));       

        if (elapsed/TICKS >= GAME_LENGTH*60) {
            elapsed = 0;
            this.end_level();
        }
    }
};

Level.update = function(){
    this.countdown();
};

答案 1 :(得分:1)

如果您希望countdowninit实例完全保密,则可以将elapsed的定义移至Level。那看起来像是:

//member variable this.elapsed has been removed
Level.init = function(){
    var elapsed = 0; // elapsed local to init
    this.countdown = function() {
        elapsed += 1;
        var remaining = (GAME_LENGTH*60) - (elapsed/TICKS);
        var minutes = Math.floor(remaining/60);
        var seconds = Math.floor(remaining%60);
        this.countdown.html(minutes + ":" + ("0"+seconds).slice(-2));       
        if (elapsed/TICKS >= GAME_LENGTH*60) {
            elapsed = 0;
            this.end_level();
        }
    };
    return this;
};

Level.update = function(){
    this.countdown();
};

对于每个实例,不能对elapsed之类的变量使用闭包,而不是每次都创建新变量。