【问题标题】:Return in recursive function doesn't exit function递归函数中的返回不退出函数
【发布时间】:2018-07-29 14:23:09
【问题描述】:

我很难修复我的递归函数,这是一个简化的工具,用于扫描 DOM 项目并在文档中的某处找到匹配元素时返回。

find: function(selector, element) {
    if(selector !== undefined) {
        if(element === undefined) {
            element = this.e;
        }
        var elements = element.childNodes;
        for(var i = 0; i < elements.length; i++) {
            if(elements[i].nodeType === 1) {
                console.log(elements[i]);
                if(this.has_class(selector, elements[i]) === true) {
                    console.log('YAY, found it', elements[i]);
                    return elements[i];
                } else {
                    if(elements[i].childNodes.length > 0) {
                        this.find(selector, elements[i]);
                    }
                }
            }
        }
    }
    return false;
}

所以该函数应该扫描给定 DOM 元素的子元素(也可能是它们的子元素)并返回找到的元素,否则更深入并重试。

这是可调试的DEMO

正如您在日志中看到的,它触发了 console.log('found');但它并没有让函数返回它,而是继续并最终返回 false (截至未找到)。怎么解决?

var tools = {

  e: document.querySelector('.breadcrumbs'),

  has_class: function(name, element) {
    if (element.className === name) {
      return true;
    }
    return false;
  },

  find: function(selector, element) {
    if (selector !== undefined) {
      if (element === undefined) {
        element = this.e;
      }
      var elements = element.childNodes;
      for (var i = 0; i < elements.length; i++) {
        if (elements[i].nodeType === 1) {
          console.log(elements[i]);
          if (this.has_class(selector, elements[i]) === true) {
            console.log('YAY, found it', elements[i]);
            return elements[i];
          } else {
            if (elements[i].childNodes.length > 0) {
              this.find(selector, elements[i]);
            }
          }
        }
      }
    }
    return false;
  }

};

console.log(tools.find('test'));
<div class="breadcrumbs" data-active="true">
  <div class="bc_navigation" onclick="events.bc_toggle(event, this);">
    <span class="bc_arrow"></span>
  </div>
  <div class="bc_content">
    <div class="bc_wrapper">
      <div class="step">
        <span class="dot"></span><a onclick="events.go_home(event, this);">Landing</a>
      </div>
      <div class="step">
        <span class="dot"></span><a href="#prematch[prematch-sport][1|0|0|0|0]">Soccer</a>
      </div>
      <div class="step">
        <span class="dot"></span><a href="#prematch[prematch-group][1|4|0|0|0]">International</a>
      </div>
      <div class="step">
        <span class="dot"></span><a class="test" href="#prematch[prematch-event][1|4|16|10|0]">Int - World Cup</a>
      </div>
      <div class="step">
        <span class="dot"></span><a>Russia - Saudi Arabia</a>
      </div>
    </div>
  </div>
</div>

【问题讨论】:

    标签: javascript recursion


    【解决方案1】:

    return 退出对您找到该元素的find 的调用,但不会展开导致它的所有调用。

    代替

    this.find(selector, elements[i]);
    

    ...在您的else 中,您需要查看是否获得了该元素,如果是,则返回:

    var result = this.find(selector, elements[i]);
    if (result) {
        return result;
    }
    

    这让它向上传播。

    更新的实时示例:

    var tools = {
    
      e: document.querySelector('.breadcrumbs'),
    
      has_class: function(name, element) {
        if (element.className === name) {
          return true;
        }
        return false;
      },
    
      find: function(selector, element) {
        if (selector !== undefined) {
          if (element === undefined) {
            element = this.e;
          }
          var elements = element.childNodes;
          for (var i = 0; i < elements.length; i++) {
            if (elements[i].nodeType === 1) {
              console.log(elements[i]);
              if (this.has_class(selector, elements[i]) === true) {
                console.log('YAY, found it', elements[i]);
                return elements[i];
              } else {
                if (elements[i].childNodes.length > 0) {
                  var result = this.find(selector, elements[i]);
                  if (result) {
                    return result;
                  }
                }
              }
            }
          }
        }
        return false;
      }
    
    };
    
    console.log(tools.find('test'));
    <div class="breadcrumbs" data-active="true">
      <div class="bc_navigation" onclick="events.bc_toggle(event, this);">
        <span class="bc_arrow"></span>
      </div>
      <div class="bc_content">
        <div class="bc_wrapper">
          <div class="step">
            <span class="dot"></span><a onclick="events.go_home(event, this);">Landing</a>
          </div>
          <div class="step">
            <span class="dot"></span><a href="#prematch[prematch-sport][1|0|0|0|0]">Soccer</a>
          </div>
          <div class="step">
            <span class="dot"></span><a href="#prematch[prematch-group][1|4|0|0|0]">International</a>
          </div>
          <div class="step">
            <span class="dot"></span><a class="test" href="#prematch[prematch-event][1|4|16|10|0]">Int - World Cup</a>
          </div>
          <div class="step">
            <span class="dot"></span><a>Russia - Saudi Arabia</a>
          </div>
        </div>
      </div>
    </div>

    这是递归函数的关键之一:当它们调用自己时,它们必须查看调用的结果并在适当的时候传播它。

    【讨论】:

    • 这是我所缺少的 :) 非常感谢,我会尽快接受这个答案,这解决了问题,我错过了尝试检查
    【解决方案2】:

    不处理对find 的递归调用的结果。您应该检查递归调用的结果值,并在递归调用找到元素时返回其值:

    find: function(selector, element) {
        if(selector !== undefined) {
            if(element === undefined) {
                element = this.e;
            }
            var elements = element.childNodes;
            for(var i = 0; i < elements.length; i++) {
                if(elements[i].nodeType === 1) {
                    console.log(elements[i]);
                    if(this.has_class(selector, elements[i]) === true) {
                        console.log('YAY, found it', elements[i]);
                        return elements[i];
                    } else {
                        var foundElement = this.find(selector, elements[i]);
                        // *** Added this check to return the located element from the recursive call
                        if (foundElement != false) {
                            return foundElement;
                        }
                    }
                }
            }
        }
        return false;
    }
    

    【讨论】:

      【解决方案3】:

      querySelector 潜在浪费

      您的tools 函数库是一项不错的工作,但它表明对querySelector 的实际工作原理缺乏了解。为了证明我的观点,你的整个程序在下面重写了

      // starting with the Document element, get the first child matching '.breadcrumbs'
      const elem =
        document.querySelector ('.breadcrumbs')
      
      // starting with `elem`, get the first child matching '.test'
      const child =
        elem.querySelector ('.test')
      
      console.log (child)
      // <a class="test" href="#prematch[prematch-event][1|4|16|10|0]">Int - World Cup</a>
      

      const elem =
        document.querySelector ('.breadcrumbs')
        
      const someChild =
        elem.querySelector ('.test')
        
      console.log (someChild)
      // <a class="test" href="#prematch[prematch-event][1|4|16|10|0]">Int - World Cup</a>
      <div class="breadcrumbs" data-active="true">
        <div class="bc_navigation" onclick="events.bc_toggle(event, this);">
          <span class="bc_arrow"></span>
        </div>
        <div class="bc_content">
          <div class="bc_wrapper">
            <div class="step">
              <span class="dot"></span><a onclick="events.go_home(event, this);">Landing</a>
            </div>
            <div class="step">
              <span class="dot"></span><a href="#prematch[prematch-sport][1|0|0|0|0]">Soccer</a>
            </div>
            <div class="step">
              <span class="dot"></span><a href="#prematch[prematch-group][1|4|0|0|0]">International</a>
            </div>
            <div class="step">
              <span class="dot"></span><a class="test" href="#prematch[prematch-event][1|4|16|10|0]">Int - World Cup</a>
            </div>
            <div class="step">
              <span class="dot"></span><a>Russia - Saudi Arabia</a>
            </div>
          </div>
        </div>
      </div>

      多个类

      在上面,querySelector 已经完成了我们需要做的所有事情,但是您的 tools.has_class 表现出另一个缺陷——元素可以有多个类。您的函数会跳过具有 class="test foo" 属性的子级。

      为了便于讨论,如果您必须自己实现此功能,您可以调整 has_class 函数以按空格分隔元素的类,然后检查每个类是否匹配 - 或者您可以使用 Element.classList其中已经包含一个contains 函数

      const elem =
        document.querySelector ('.test')
      
      console.log (elem.classList)
      // { DOMTokenList [ "foo", "test", "bar" ] }
      
      console.log (elem.classList.contains ('foo'))
      // true
      
      console.log (elem.classList.contains ('test'))
      // true
      
      console.log (elem.classList.contains ('dog'))
      // false
      &lt;div class="foo test bar"&gt;&lt;/div&gt;

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-01-12
        • 2015-10-22
        • 1970-01-01
        • 2020-04-28
        • 2012-09-21
        • 2016-06-30
        相关资源
        最近更新 更多