I am trying to use setTimeout()
inside a class function in JavaScript. The setTimeout()
is supposed to trigger another method in the sa开发者_开发百科me Class, so the function I am passing it is written as window.setTimeout("this.anotherMethod", 4000)
. That bring the problem: this
references the calling Object, in the case of setTimeout()
it is window
. How can I use enclosures to return a reference to the Class Object itself?
myObject = function(){
this.move = function(){
alert(this + " is running");
}
this.turn = function(){
alert(this + " is turning");
}
this.wait = function(){
window.setTimeout("this.run" ,(1000 * randomNumber(1,5)));
}
this.run = function(){
switch(randomNumber(0,2)){
case 0:
this.move();
break;
case 1:
this.turn();
break;
case 2:
this.wait();
}
}
}
You can do this:
var that = this;
setTimeout(function () {
that.doStuff();
}, 4000);
You can also bind
for more succinct code (as originally pointed out by @Raynos):
setTimeout(this.doStuff.bind(this), 4000);
bind
is a standard library function for exactly this coding pattern (ie capturing this
lexically).
You can also bind a function to scope.
setTimeout(this.run.bind(this) ,(1000 * randomNumber(1,5)));
Be warned Function.prototype.bind
is ES5
this
can be problematic in javascript, as you've discovered.
I usually work around this by aliasing this
inside the object so that I can use the alias whenever I need a reference back to the containing object.
MyObject = function ()
{
var self = this;
// The rest of the code goes here
self.wait = function(){
window.setTimeout(self.run ,(1000 * randomNumber(1,5)));
}
}
this.wait = function(){
var self = this;
window.setTimeout(function() { self.run() } ,(1000 * randomNumber(1,5)));
}
So you store the reference to the object you're calling .run on in a local variable ('self').
class A{
setTimeout(()=>{
// here this != undefined because of arrow function
},500);
}
this
is sensitive to the context in which it is called. When you pass a string to setTimeout
then that is eval
ed in a completely different context.
You need to preserve the current value of this
(by copying it to a different variable) and maintain the scope (by not using (implied) eval
).
this.wait = function(){
var self = this;
setTimeout(function () { self.run() },
(1000 * randomNumber(1,5))
);
}
At the top of your main myObject
make a new reference to the current value of this
:
var self = this;
and then create a closure for your timer callback that uses that new reference instead of the global object that setTimeout
will use as the default context in callbacks:
setTimeout(function() {
self.run();
}, 4000);
var timeoutID = window.setTimeout(func, delay, [param1, param2, ...]);
inside func
, this
always refer to the global object. you can pass in the current object into func,
var timeoutID = window.setTimeout(func, delay, this);
function func(that) {...}
unfortunately it does NOT work in IE
Note that passing additional parameters to the function in the first syntax does not work in Internet Explorer.
you can just use the arrow function syntax:
setTimeout(() => {
this.doStuff();
}, 4000);
Have you tried;
window.setTimeout("myObject.run" ,(1000 * randomNumber(1,5)));
You can use this code instead, which works in all modern browsers -
setTimeout(function(thisObj) {thisObj.run();},1000,this);
Ref: http://klevo.sk/javascript/javascripts-settimeout-and-how-to-use-it-with-your-methods/
Shorter way. Without anonymous func.
var self = this;
setTimeout(self.method, 1000);
It is not recommended to use setTimeout or setInterval using strings
setTimeout("myFunction()", 5000);
//this is the same as
setTimeout(function(){ eval("myFunction()"); }, 5000)); //<-- eval == BAD
Ran into a more complex situation...class A has a member of type B and a method that calls setTimeout which calls a method on class B. Solved as follows:
class A {
constructor(b) {
this.b = b;
}
setTimer(interval) {
setTimeout(this.b.tick.bind(this.b), interval);
}
}
class B {
constructor(name){
this.name = name;
this.ele = window.document.getElementById('B');
}
tick() {
console.log(this);
this.ele.innerText += ' ' + this.name;
}
}
Which bound A.b to this within B.tick and worked.
Here's a fiddle with bind
: https://jsfiddle.net/jrme9hyh/
And one without bind
which fails: https://jsfiddle.net/2jde8tq3/
精彩评论