问题
I have this simple code:
var Modules = (function() {
'use strict';
return {
TIMER: function (){
var timer = null;
return {
time: 100,
init: function() {
this.counter();
this.timer = window.setInterval(this.counter, 1000);
},
counter: function() {
this.time -= 1;
if (this.time <= 0) {
window.clearInterval(this.timer);
alert('Time expired');
}
console.log(this.time);
this.viewer();
},
viewer: function() {
document.getElementById('timer').innerHTML = this.time;
}
}
}
};
}());
Modules.TIMER().init();
<div id="timer"></div>
And something is wrong because I got 2 errors:
this.viewer is not a function
and
NaN of this.time
What it wrong with my design pattern running over interval?
After extend TIMER to reset method:
reset: function() {
this.time = 100;
}
and call it outside as:
Modules.TIMER().reset();
?
I got
this.time is not defined
.
Or inside init:
jQuery("body").on('keyup mouseup', function (e) {
this.reset();
});
I got error:
this.reset() is not s function.
回答1:
Your issue is coming from this line:
this.timer = window.setInterval(this.counter, 1000);
When you are invoking the callback in the setInterval
method, the this
in the callback function no longer refers to your TIMER
object, but the window
.
Solution A: Use .bind(this)
to bind lexical this
to callback
You will need to bind the current context to the callback:
this.timer = window.setInterval(this.counter.bind(this), 1000);
var Modules = (function() {
'use strict';
return {
TIMER: function (){
var timer = null;
return {
time: 100,
init: function() {
this.counter();
this.timer = window.setInterval(this.counter.bind(this), 1000);
},
counter: function() {
this.time -= 1;
if (this.time <= 0) {
window.clearInterval(this.timer);
alert('Time expired');
}
console.log(this.time);
this.viewer();
},
viewer: function() {
document.getElementById('timer').innerHTML = this.time;
}
}
}
};
}());
Modules.TIMER().init();
<div id="timer"></div>
Solution B: Use ES6 arrow function in setInterval
callback
Note: Personally, I prefer this solution because it uses ES6, but if you are still supporting legacy browsers and do not want to transpile your JS, this might not be the best solution.
Another alternative will be using an arrow function in the callback of setInterval
, instead of assigning the this.counter
function as the callback directly:
this.timer = window.setInterval(() => this.counter(), 1000);
The arrow function preserves the lexical this
, so when this.counter()
is invoked it will be using the same context, i.e. the internal this
will be referencing your TIMER
object.
var Modules = (function() {
'use strict';
return {
TIMER: function (){
var timer = null;
return {
time: 100,
init: function() {
this.counter();
this.timer = window.setInterval(() => this.counter(), 1000);
},
counter: function() {
this.time -= 1;
if (this.time <= 0) {
window.clearInterval(this.timer);
alert('Time expired');
}
console.log(this.time);
this.viewer();
},
viewer: function() {
document.getElementById('timer').innerHTML = this.time;
}
}
}
};
}());
Modules.TIMER().init();
<div id="timer"></div>
回答2:
I found answer for reset from outside. I changed TIMER like this:
TIMER: (function (){
// all stuff
}())
Then call is like: Modules.TIMER.reset();
and then it works as expected.
来源:https://stackoverflow.com/questions/56661554/this-method-is-not-function-with-setinterval