我最近看到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的回答)。但是仍然无限循环。
答案 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,这些属性是可枚举的。