简体   繁体   中英

Recursive JavaScript Method Scope

I have this recursive function that is suppose to go through a JSON object to find all the subgroups. Here is what I have:

var subgroupIds = [];
this.getSubGroups = function (groupId) {
    this.getGroups("groupId="+groupId, function(groups) {
        if ($.isEmptyObject(groups)) {
            return subgroupIds;
        } else {
            $.each(groups, function(index,group) {
            subgroupIds.push(group.id);
                this.getSubGroups(group.id);
            });
        }
    });
}

...where getGroups is an asynchronous function that returns all the groups.

My problem is when it gets to the recursive call, I get the error that says:

Uncaught TypeError: Object #<Object> has no method 'getSubGroups' 

I'm guessing it's a scope issue but I can't figure out what's wrong with it. Any ideas?

EDIT:

As Bergi pointed out, I need to have a callback. This is what I have now:

var subgroupIds = [];
var that = this;
this.getSubGroups = function (groupId,callback) {
    this.getGroups("groupId="+groupId, function(groups) {
        if ($.isEmptyObject(groups)) {
            return;
        } else {
            $.each(groups, function(index,group) {
            subgroupIds.push(group.id);
                callback(group.id);
                that.getSubGroups(group.id);
            });
        }
    });
}

The trouble now is when I call the callback, it says the function is undefined.

Ultimately, I want to return an array of sub group.id's. I'm not sure how to get there...

This is because thi inner function, defined in each does not belong to this instance of your class/object, isntead it refers to the callback function itself. To bypass this problem, at the object constructor, I put the folowing:

Solution in general:

function SimpleClass() {
     var _this = this;    //Private property, will be shared in all functions defined in the class
     this.value = "Hello";
     this.value2 = " world!";

     function say() {        //private function - use _this (this means something else here)
         return _this.say;   //Gets property of the object
     }
     this.say = function() { //Public function - use this
        alert(this.value+say());  //Here, we can use both this and _this
     }

}

Fix for your code:

var _this = this;
var subgroupIds = [];
this.getSubGroups = function (groupId) {
    this.getGroups("groupId="+groupId, function(groups) {
        if ($.isEmptyObject(groups)) {
            return subgroupIds;
        } else {
            $.each(groups, function(index,group) {
            subgroupIds.push(group.id);
                _this.getSubGroups(group.id);
            });
        }
    });
}

Your this is not the same this as before. Perhaps save this into a variable name more relevant to the object it's referencing (I'll use someObject ...) before your first getSubGroups call, and then call someObject.getSubGroups later.

var subgroupIds = [];
var someObject = this;
someObject.getSubGroups = function (groupId) {
    this.getGroups("groupId="+groupId, function(groups) {
        if ($.isEmptyObject(groups)) {
            return subgroupIds;
        } else {
            $.each(groups, function(index,group) {
            subgroupIds.push(group.id);
                someObject.getSubGroups(group.id);
            });
        }
    });
}

The reason why this happens is because your function is called from the $.each implementation. I would refer to MDN docs of "this" for more details.

The inner this refers to $.each callback, you can do something like this:

var subgroupIds = [];
var self = this;
self.getSubGroups = function (groupId) {
    self.getGroups("groupId="+groupId, function(groups) {
        if ($.isEmptyObject(groups)) {
            return subgroupIds;
        } else {
            $.each(groups, function(index,group) {
            subgroupIds.push(group.id);
                self.getSubGroups(group.id);
            });
        }
    });
}

This is because this is defined at a function level, and is bound to the item in the array you're iterating over for the inner function in this case. this within the inner function does not refer to the same thing as this within the outer function. You can get around this by setting var that = this; on the outer level and referencing that from within the inner function

var subgroupIds = [];
var that = this;
this.getSubGroups = function (groupId) {
    this.getGroups("groupId="+groupId, function(groups) {
        if ($.isEmptyObject(groups)) {
            return subgroupIds;
        } else {
            $.each(groups, function(index,group) {
            subgroupIds.push(group.id);
                that.getSubGroups(group.id);
            });
        }
    });
}

For more see How do closures work

And here for How this works in JS

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM