如何防止无限递归

时间:2011-01-12 22:20:12

标签: javascript jquery algorithm recursion

我试图以递归方式打印出 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?谢谢,

5 个答案:

答案 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("&nbsp;&nbsp;") + ">";
    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("&nbsp;&nbsp;") + ">";

    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,因为在打印对象图的情况下,您可能想要查看继承的属性。