Why "this" is pointing to something else in Javascript recursive function?
Consider the following JavaScript code which has a recursive boom
function
function foo() {
this.arr = [1, 2, 3, 4, 5];
this.output = "";
this.get_something = function(n, callback) {
this.output += "(" + n + ")";
$.get("...", function(开发者_运维知识库result) {
// do something with 'result'
callback();
});
};
this.boom = function() {
if (this.arr.length > 0) {
this.get_something(this.arr.shift(), this.boom);
}
};
this.boom();
document.write("output = [" + this.output + "]");
}
var f = new foo();
When get_something
is executed for the first time and callback()
is called, this.arr
is not available anymore (probably because this
is pointing to something else now).
How would you solve this ?
The problem is in Ajax requests and function call context
The problem you're having is function call context when boom
gets called after you've received data from the server. Change your code to this and things should start working:
this.get_something = function(n, callback) {
var context = this;
this.output += "(" + n + ")";
$.get("...", function(result) {
// do something with 'result'
callback.call(context);
});
};
Additional suggestion
I know that your array has only few items but this code of yours is probably just an example and in reality has many of them. The problem is that your recursion will be as deep as your array has elements. This may become a problem because browsers (or better said Javascript engines in them) have a safety check of recursion depth and you will get an exception when you hit the limit.
But you will also have to change your document write and put it in the boom function since these calls are now no more recursive (it they were at all in the first place).
this.boom = function() {
if (this.arr.length > 0) {
this.get_something(this.arr.shift(), this.boom);
}
else {
document.write("output = [" + this.output + "]");
}
};
After some deeper observation
Your code actually doesn't execute in recursion unless you use synchronous Ajax requests which is by itself a show stopper and everyone would urge you to keep the async if at all possible. So basically my first solution with providing function call context would do the trick just fine.
If you're using async Ajax requests your document.write
shouldn't display correct result, because it would be called too early. My last suggested boom
function body would solve this issue as well.
this.get_something(this.arr.shift(), this.boom.bind(this));
Now you've bound this
to be this
. The context inside the boom
function will always be what you expect it to be.
.bind
is not supported in non-modern browsers so use the compatibility implementation.
As a side note $.proxy
can do most of what .bind
offers. So it may be simpler for you to use
this.get_something(this.arr.shift(), $.proxy(this.boom.bind, this));
.bind
is currently supported by IE9, FF4 and Chrome.
.bind
, jQuery.proxy
精彩评论