不需要的 Javascript 效果:原型在实例之间共享闭包
Unwanted Javascript effect: Prototype is sharing closure between instances
我正在制作一个带有定时关卡的游戏。一个关卡时长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
变量不会被重置。
换句话说,用户在还剩 30 秒时提前退出关卡。然后用户再次启动关卡,但现在,倒数计时器仍显示 30 秒,而不是整整 2 分钟。
有没有一种优雅的方法可以解决这个问题并且仍然使用闭包?或者我必须恢复到我以前的解决方案,忘记闭包,并将 elapsed
设为成员变量吗?
Is there an elegant way to fix this and still use a closure? Or must I revert to my previous solution, forget closures, and make elapsed a member variable?
它必须是特定于实例的;这并不意味着它必须是对象上的 属性 。您可以从 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();
};
如果您想让 elapsed
对 Level
实例完全私有,您可以将 countdown
的定义移动到 init
中。看起来像:
//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
这样的变量使用闭包。
我正在制作一个带有定时关卡的游戏。一个关卡时长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
变量不会被重置。
换句话说,用户在还剩 30 秒时提前退出关卡。然后用户再次启动关卡,但现在,倒数计时器仍显示 30 秒,而不是整整 2 分钟。
有没有一种优雅的方法可以解决这个问题并且仍然使用闭包?或者我必须恢复到我以前的解决方案,忘记闭包,并将 elapsed
设为成员变量吗?
Is there an elegant way to fix this and still use a closure? Or must I revert to my previous solution, forget closures, and make elapsed a member variable?
它必须是特定于实例的;这并不意味着它必须是对象上的 属性 。您可以从 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();
};
如果您想让 elapsed
对 Level
实例完全私有,您可以将 countdown
的定义移动到 init
中。看起来像:
//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
这样的变量使用闭包。