我试图以递归方式打印出 jQuery 的内容。我打算用它来分析现有的jQuery实例(在页面中加载),对着已知的“jQuery签名”的小型数据库,来确定已经做了哪些更改(即已经加载了哪些插件,修改了函数等等) )。
要做到这一点,我有这个小功能:
function recurse(obj, iter){
var padding = (new Array(iter + 1)).join(" ") + ">";
for (var i in obj){
document.writeln(padding + i + "<br/>");
if (iter < 5)
recurse(obj[i], iter + 1);
}
}
执行此操作时:
recurse(jQuery, 1);
我得到这样的东西:
>prototype
>init
>prototype
>init
>prototype
>selector
>jquery
>0
.... On and on and on .....
我的问题是,在一开始,您可以看到prototype
然后init
一遍又一遍地重复。它在5深处停止的唯一原因是if (iter < 5)
检查。如果不存在限制,它将永远重现[原文如此]。迭代限制有帮助,但是如果有一个关键功能6深度怎么办?基本上,我不知道我应该做什么迭代限制,或者根本应该有一个。
相反,我认为必须有某种算法可以防止永无止境的递归。这样的算法存在吗?或者我应该改变我如何遍历jQuery?谢谢,
答案 0 :(得分:4)
你可以跟踪你已经看过的价值,并在你再次看到它时拯救。
function recurse(obj) {
var marker = '__' + new Date().getTime() + '__';
function r(obj, iter) {
if (marker in obj) return;
var padding = (new Array(iter + 1)).join(" ") + ">";
obj[marker] = true;
for (var i in obj) {
if (!obj.hasOwnProperty(i) || i === marker) continue;
document.writeln(padding + i + "<br/>");
recurse(obj[i], iter + 1);
}
}
r(obj, 0);
}
现在这当然有一个缺点,就是让你的遍历对象图中充满了额外的属性,但是对于一些不会有问题的应用程序。同时使用时钟制作“独特”标记实在是太蹩脚了;你可能只想使用一个计数器,或者只是一个固定的无意义的字符串。
编辑 - 此外,另一个问题(原始代码中也存在)是,这应该检查“obj”值是否真的是对象。如果他们是标量,那么真的没有必要做任何事情。您只需要在检查标记后立即执行“typeof”检查,如果您看到null,数字,字符串或布尔值,则返回。
答案 1 :(得分:1)
您的递归缺少基本情况。请参阅Recursion的定义。您引入了(depth < 5)
的任意基本案例。也许使用数组的长度,或者正如Pointy指出的那样,hasOwnProperty检查跳过递归调用。
答案 2 :(得分:0)
除非我遗漏了某些内容,否则您需要做的就是跳过字符串(如果您使用现代浏览器允许索引字符串,否则无关紧要)和init函数,它是jQuery自引用你无限递归
function recurse(obj, iter){
var padding = (new Array(iter + 1)).join(" ") + ">";
for (var i in obj){
document.writeln(padding + i + "<br/>");
if (i != 'init' && typeof obj[i] != 'string')
recurse(obj[i], iter + 1);
}
}
答案 3 :(得分:0)
正如几个答案所说:您的算法必须包含适当的中断条件以防止无限递归。
但是如果您无法控制算法的输入怎么办?
在这种情况下,或仅出于开发目的,您可以使用以下方法:
function acmeRecursion(){
var count = localStorage.getItem("count");
if(count>50){
throw new Error("recursion limit exceeded");
}
count++;
//put your logic here
}
答案 4 :(得分:-1)
建立Pointy的答案(理想情况下,这将是一个评论,但唉,代码在这些方面不起作用),更好的解决方案可能是将对象传递给将跟踪的recurse函数你已经看过的东西。像这样:
var recurse = function(obj)
{
var seen = {};
var inner = function(obj, padding)
{
for (var i in obj)
{
if (!(obj[i] in seen))
{
document.writeln(padding + i + '<br />');
seen[obj[i]] = true;
inner(obj[i], padding + ' ');
}
}
};
return inner(obj, '');
};
为了简单起见,使用闭包而不是参数传递seen
对象,但基本概念是相同的。
这种方法的优点是不会为您正在遍历的对象添加额外的属性。
编辑:我想解释一下,但忘记了。我在这里没有使用hasOwnProperty
,因为在打印对象图的情况下,您可能做想要查看继承的属性。