I'm trying to convert the following function to a class in JavaScript (original function from CMS created here),
function Countdown(options) {
var timer,
instance = this,
seconds = options.seconds || 10,
updateStatus = options.onUpdateStatus || function () {},
counterEnd = options.onCounterEnd || function () {};
function decrementCounter() {
updateStatus(seconds);
if (seconds === 0) {
counterEnd();
instance.stop();
}
seconds--;
}
this.start = function () {
clearInterval(timer);
timer = 0;
seconds = options.seconds;
timer = setInterval(decrementCounter, 1000);
};
this.stop = function () {
clearInterval(timer);
};
}
With this usage,
var myCounter = new Countdown({
seconds:5, // number of seconds to count down
onUpdateStatus: function(sec){console.log(sec);}, // callback for each second
onCounterEnd: function(){ alert('counter ended!');} // final action
});
myCounter.start();
My attempt is,
Countdown: class {
constructor(options) {
this.seconds = options.seconds || 10;
this.updateStatus = options.onUpdateStatus || function () { };
this.counterEnd = options.onCounterEnd || function () { };
this.instance = this;
this.timer = null;
}
decrementCounter() {
this.updateStatus(this.seconds);
if (this.seconds === 0) {
this.counterEnd();
this.instance.stop();
}
this.seconds--;
}
start() {
clearInterval(this.timer);
this.timer = 0;
this.timer = setInterval(this.decrementCounter, 1000);
}
stop () {
clearInterval(this.timer);
}
}
And calling it like this,
var counter = new Countdown({
seconds:5, // number of seconds to count down
onUpdateStatus: function(sec) {
$("#timer").text(sec);
}, // callback for each second
onCounterEnd: function() {
closeModal();
} // final action
});
counter.start();
It's throwing this error in decrementCounter()
,
Uncaught TypeError: this.updateStatus is not a function
What am I doing wrong?
this
refers to the global object which doesn't have updateStatus
, because the setInterval
function doesn't preserve the this
-context.
Use bind in the constructor to fix it:
Bind creates a new function that will have
this
set to the first parameter passed tobind()
.
constructor(options) {
this.seconds = options.seconds || 10;
this.updateStatus = options.onUpdateStatus || function() {};
this.counterEnd = options.onCounterEnd || function() {};
this.instance = this;
this.timer = null;
this.decrementCounter = this.decrementCounter.bind(this);
}