尝试搜索对象时无限循环

时间:2012-04-27 18:40:16

标签: javascript

我最近看到this question被问到哪个OP想要找到对象属性的路径,所以我在psuedocode中回答它,说明我没有足够的时间来实际编写解决方案。然而,这个问题对我来说非常有趣,无论如何我都试图写一个解决方案。这是我到目前为止所提出的:

function isEmpty(obj) {
    for (var prop in obj) {
        if (Object.prototype.hasOwnProperty.call(obj, prop)) {
            return false;
        }
    }
    return true;
}

function Node(obj, parent, searchTarget) {
    this.parent = parent;
    this.obj = obj;
    this.searchTarget = searchTarget;

    this.searchNode = function() {
        if(this.obj == this.searchTarget) {

            //return this.reconstructPathRecursive();
        }

        if (!isEmpty(this.obj)) {
            var children = [];

            for (prop in this.obj) {
                if (this.obj.hasOwnProperty(prop)) {
                    children.push(new Node(this.obj[prop], this, searchTarget));
                }
            }

            var path;
            for(var i = 0, len = children.length; i < len; i++) {
                path = children[i].searchNode();
                if(path) return path;
            }
        }
    }

    this.reconstructPathRecursive = function() {
        var path = [this], curObj = this.parent;

        while (curObj != undefined) {
            path.push(curObj);
            curObj = curObj.parent;
            if(curObj == undefined) break;
        }

        return path;
    }

    this.findPath = function() {
        return this.searchNode();
    }
}

var myObj = {
    nullRoot: "gotcha!",
    path1: {
        myFunc: function() {
            alert("Success!");
        }
    }
}

function findFunctionPath(obj, func) {
    return new Node(obj, undefined, func).findPath();
}
var thisFunc = myObj.path1.myFunc;
    console.log("--");

console.log(findFunctionPath(myObj, thisFunc));

我的想法是,我会在Node对象上调用this.searchNode()来表示每个对象的属性。 searchNode()会在每个结果属性节点上调用自身,并在每个子节点上将当前对象作为parent传递。如果我找到要搜索的函数,我会使用每个节点上的parent属性调用reconstructPathRecursive(),这几乎就是这样。

但是,我得到了“超出最大调用堆栈大小”。运行此live test时出错。我认为这意味着我意外地以某种方式写了一个无限循环。我逻辑中的缺陷在哪里,无限循环在哪里潜入? console.log显示searchNode被一遍又一遍地调用,而我只是在对象不为空时调用它,并且我不会在任何地方给对象提供引用(I不要以为......),所以我真的很难过。

编辑:我稍微更新了代码,将isEmpty从Node函数更改为全局函数,以便我可以在{{1} this.obj上调用它功能。以前,它只会在节点上调用它(它将始终至少有两个属性,从而导致无限循环),而不是引用的对象。这是固定的,但错误仍然存​​在。

另一个编辑:找到并更正了其他错误(请参阅Satyajit的回答)。但是仍然无限循环。

2 个答案:

答案 0 :(得分:2)

属性nullRoot是一个字符串,而不是一个javascript对象。在其上运行isEmpty函数将永远不会返回false并将其置于无限循环中。你把“陷阱”作为它的价值几乎是预言。

答案 1 :(得分:1)

这将失败

var arr = [];
arr[0] = arr;

因为它包含自己的孩子。 Node函数最终创建一个无界的节点列表,其中父节点与子节点相同。

要处理循环对象图,您需要跟踪已访问过的内容并避免重新访问它。这在JavaScript中很难,因为你没有对象集。

您可以保留您访问过的对象列表,并检查其中是否显示obj,或者您可以尝试使用特殊对象属性( breadcrumb )来判断你是否去过一个物体。如果您没有正确清理或其他人使用该属性,或其他代码使用EcmaScript 5冻结,则痕迹导致失败。


编辑:

当您在孩子中找到路径时,您也应该打破子循环。

path = children[i].searchNode();

应该是

 path = children[i].searchNode();
 if (path) { return path; } 

编辑:

正如Satyajit所指出的那样,"gotcha"是属性值。

"gotcha"[0] === "g""g"[0] === "g"以来,您需要很长时间才能达到一个周期。

当我这样做时

alert("gotcha".hasOwnProperty(0));
for (var k in "gotcha") { alert(k); }

在最近的Chrome中,我收到提醒“真实”,“0”,“1”,......,“5”。

这是现代浏览器的标准行为,如15.5.5.2部分所述:

  

String对象使用用于其他本机ECMAScript对象的[[GetOwnProperty]]内部方法的变体(8.12.1)。此特殊内部方法用于为与String对象的各个字符对应的命名属性添加访问权限。

并且由于该部分中的子弹9,这些属性是可枚举的。