Is it possible to somehow invoke a non-global function (i.e. a method of a class) from a timer?
The following example shows what I'd like to achieve:
function MyClass() {
var msg = 'no message';
// shows an alert after some delay
this.delayedAlert = function(message) {
m开发者_StackOverflow社区sg = message;
// global function works:
//window.setTimeout('globalShow("'+message+'")', 1000);
// this obviously also works:
//this.show();
// this doesn't work: *************
// I'd like to invoke the show() method of this class after some delay
window.setTimeout('this.show()', 2000);
};
this.show = function() {
alert(msg);
};
}
$(document).ready(function() {
$('p').click(function() {
var c = new MyClass();
c.delayedAlert('hello');
});
});
function globalShow(msg) {
$('#hello').html(msg);
}
You can find a running sample here: http://jsbin.com/aqako5
function MyClass() {
var msg = 'no message';
// shows an alert after some delay
this.delayedAlert = function(message) {
msg = message;
window.setTimeout($.proxy(this.show, this), 2000);
};
this.show = function() {
alert(msg);
};
}
Please use functions in setTimeout
. Strings call eval
and eval
is like goto
. I.e. the raptors get you.
What's actually happening is that inside the function in setTimeout the this
value is the default this
value, i.e. window. You need to pass in a proxied method so that this
is what you want this
to be.
An alternative pattern to proxying in the correct this
value is :
function MyClass() {
var msg = 'no message';
var that = this;
// shows an alert after some delay
this.delayedAlert = function(message) {
msg = message;
window.setTimeout(function() {
that.show();
}, 2000);
};
this.show = function() {
alert(msg);
};
}
This involves "caching" the correct this
value in a separate variable. You still can't pass in that.show
directly because that function will be invoked without knowing what this
is.
This would help. Also to improve your class: if your class is not instantiated, it may cause problems. What would happen if someone called your class like so:
var myClass = MyClass();
The this
inside MyClass
would now be window, causing all sorts of problems and adding your properties to the global namespace.
To fix this, you need to make sure that when your class function is being called, that it was done with the new
operator. You need to check inside MyClass
that if this isn't a new instance of MyClass
to force it to be a new instance. With my code below, you can call this method either of the following ways and have them both function the same way:
var myClass = MyClass();
// or
var myClass = new MyClass();
Change your class to this:
function MyClass() {
// if this isn't an instance, make it so:
if (!(this instanceof MyClass)) {
return new MyClass();
}
var _this = $(this);
var msg = 'no message';
var show = function() {
alert(msg);
};
// shows an alert after some delay
this.delayedAlert = function(message) {
msg = message;
window.setTimeout(show, 2000);
};
}
You can also improve this code by not creating a new function definition every time an instance of MyClass
is created. Add your methods to the prototype of MyClass
:
(function(window) {
var MyClass = function() {
// if this isn't an instance, make it so:
if (!(this instanceof MyClass)) {
return new MyClass();
}
};
var msg = 'no message';
var show = function() {
alert(msg);
};
MyClass.prototype.delayedAlert = function(message) {
msg = message;
window.setTimeout(show, 2000);
};
window.MyClass = MyClass;
})(window);
You could declare an anonymous function:
window.setTimeout(function () {
this.show();
}, 2000);
And, depending on the function, you could just pass the function itself to setTimeout
:
window.setTimeout(this.show, 2000);
When you pass a string to setTimeout
, you're basically telling the browser to wait a certain amount of time, and then eval
the string.
When the browse eval
s "this.show()"
, it thinks that this
refers to the global object, or the window
.
The first suggestion uses closures, the second just passes the function itself (without executing it!), which we can do because in JavaScript, functions are first-class objects and can be treated just like any other variable.
Just keep in mind that when you use the second solution, you are divorcing the method from its context. If you try the following...
var msg = 'sadface';
var myObj = {
msg: 'hello world',
greet: function () {
alert(this.msg);
}
};
window.setTimeout(myObj.greet, 2000);
...then the this
in greet
will no longer refer to myObj
. It will instead point to the window
. It will alert sadface
, not hello world
.
Good luck.
精彩评论