Recursive JavaScript Method Scope

泪湿孤枕 提交于 2019-12-24 09:58:54

问题


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...


回答1:


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




回答2:


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);
            });
        }
    });
}



回答3:


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);
            });
        }
    });
}



回答4:


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.



来源:https://stackoverflow.com/questions/15122378/recursive-javascript-method-scope

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!