开发者

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

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜