具有递归函数的函数JS

时间:2014-02-15 03:45:18

标签: javascript

好吧,我意识到这对你们中的许多人来说可能是非常初学者,但是我希望有人可以解释一下这种方式我可以解决这个问题。我的问题围绕着我所听到的功能JavaScript的基础 - 递归。我正在开发一个个人项目,并为一个人找到了一个很好的用例,但我仍然无法理解正在发生的事情 - 需要一种可视化的思维方式。

所以,这是一个例子。正在解决的问题是一个简单的辅助函数,用于查找与特定标记名匹配的DOM中的下一个兄弟(假设在调用函数时传入当前元素和标记名,即findSibling(this, 'DIV'))。

var findSibling = function(el, tagName) {
  if (el.nextSibling.tagName === tagName.toUpperCase())
    return el.nextSibling;
  else
    return findSibling(el.nextSibling, tagName);
}

好的,这样可行!大!但是,我花了很长时间才到这里,它真的不应该。我尝试过白板,最好的我能理解的是这样的事情正在发生:

findSibling(<span>,div)▸sitSibling(<span>,div)▸sitSibling(<span>,div)▸<div>

假设我们有类似这样的HTML:

<div></div>
<span></span>
<span></span>
<span></span>
<div></div>

任何人都可以帮助我更多地想象这一点吗?您在第一次学习这个概念时可能会使用的任何提示/技巧?我只是在寻找那个灯泡......

另外,我坚持了一段时间的事情是第二次返回声明。为什么我不能只调用else中的函数?为什么我需要退货?好像它只是用兄弟元素调用函数。

谢谢!

4 个答案:

答案 0 :(得分:1)

我喜欢将镜像效果或Droste效果视为递归,基于以下图片:

enter image description here

每个级别深入挖掘下一个级别,直到达到极限。在您的情况下,找到具有所需标签名称的兄弟。

基本上你的基本代码是第一张图片,第一个递归是第一层。它会更深入,直到达到目标。

答案 1 :(得分:1)

  

任何人都可以帮助我更多地想象这一点吗?你的任何提示/技巧   可能在第一次学习这个概念时使用过吗?

考虑一个迭代版本可能有助于理解:

function findSibling(el, tagName) {
  while (el = el.nextSibling) {
    if (el.tagName == tagName.toUpperCase()) {
      return el;
    }
  }
}
  

此外,我坚持了一段时间的第二件事是第二次回归   声明。为什么我不能只调用else中的函数?我为什么会   需要回报吗?

作为一般规则的递归函数将具有递归调用和退出条件。定义本身是递归的。在您的代码中,退出条件是找到tagName

如果你不理解递归,解释递归很难。维基百科对可视化有很好的解释。 http://en.wikipedia.org/wiki/Recursion

修改:请同时查看此问题https://softwareengineering.stackexchange.com/questions/25052/in-plain-english-what-is-recursion

答案 2 :(得分:1)

You call a function findSibling to find 'tag'
   But it doesnt find tag so it calls function findSibling to find 'tag'
       But it doesnt find tag so it calls function findSibling to find 'tag'
           But it doesnt find tag so it calls function findSibling to find 'tag'
               It retrns tag to its caller
           It returns tag to its caller
       It returns tag to its caller
   It returns tag to its you.
You have tag.

我认为你能理解的最好的是这个特定问题的正确答案。要在嵌套 DOM中查找内容,只需稍微复杂一些思考,但它的概念相同......而且代码几乎相同。

答案 3 :(得分:1)

首先解释你的第二个问题,让我们稍微重写你的功能:

var findSibling = function(el, tagName) {
    var match;
    if (el.nextSibling.tagName === tagName.toUpperCase()) {
        match = el.nextSibling;
    } else {
        match = findSibling(el.nextSibling, tagName);
    }
    return match;
}

原始代码中的两个返回都做同样的事情,返回您正在搜索的标记。每种情况的不同之处在于如何计算匹配。

要回答您的第一个问题,让我们以不同的方式查看您的代码。只要有函数,就可以随时用函数代码替换函数调用,并正确替换参数。例如:

function hello(text) {
    alert('Hello ' + text);
}
hello('to you.');

相当于

alert('Hello to you.');

所以让我们用你的递归函数做到这一点:

if (el.nextSibling.tagName === tagName.toUpperCase()) 
    return el.nextSibling;
else 
    if (el.nextSibling.nextSibling.tagName === tagName.toUpperCase()) 
        return el.nextSibling.nextSibling;
    else 
        if (el.nextSibling.nextSibling.nextSibling.tagName === tagName.toUpperCase()) 
            return el.nextSibling.nextSibling.nextSibling;
        else 
            etcetera...

从这里你应该能够看到递归函数如何隐藏循环本身。这也表明了递归函数的危险 - 他们可以不停地自我调用。这让我想知道 - 如果el没有nextSibling,您的功能会怎样?