最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 如何跳出递归函数,不再执行???

    正文概述 掘金(答案cp3)   2021-07-23   890

    「本文已参与好文召集令活动,点击查看:后端、大前端双赛道投稿,2万元奖池等你挑战!」

    前言

    前几天看到一个面试题:

    请实现一个 find 函数,功能等同于 document.getElementById

    分析:

    就是一个查找节点函数,然后节点是可以嵌套节点的,而你是不知道会嵌套几层,正常做法是使用递归查找,直到递归到最底层。

    三下五除二,刷刷刷,代码写就出来了。

    代码解析

    代码如下:

    解法1:

    function findId (id, node= document.body, map = {}) {
      const children = node.children
      for(let i = 0; i <children.length; i++) {
        if (!map[children[i].id]) map[children[i].id] = children[i]
        findId(id, children[i], map)
      }
      return map[id] || null
    }
    
    findId('header') // <div id="header"></div>
    findId('header1') // 如果页面没有该元素,则return null
    

    解析:

    主要用一个对象做map,然后遍历和递归遍历把所有节点塞到map中,以id为key,节点为value, 最后把需要找的id去map里面找,然后return。

    解法2:

    let findId = (function (findNode) {
      return function fn (id, node = document.body) {
        const children = node.children
        for (let i = 0; i < children.length; i++) {
          if (children[i].id === id) findNode = children[i]
          else findNode = fn(id, children[i])
        }
        return findNode
      }
    })(null)
    

    解析:

    利用一个立即执行匿名函数,用findNode(null)做参数,然后返回一个新函数,传入id,遍历和递归遍历, 如果找到该id一样的节点,就赋值给findNode, 最后返回。

    下面来验证一下:

    html结构:

      <div id="header">
        <div id="header-child"></div>
      </div>
      <div id="content">
        <div id="content-child">1</div>
        <div id="content-child">2</div>
      </div>
      <div id="footer">
        <div id="footer-child"></div>
      </div>
    
    function findId (id, node= document.body, map = {}) {
      const children = node.children
      for(let i = 0; i <children.length; i++) {
        if (!map[children[i].id]) map[children[i].id] = children[i]
        findId(id, children[i], map)
      }
      return map[id] || null
    }
    findId('header') // <div id="header"></div>
    findId('header1') // null
    
    let findId = (function (findNode) {
      return function fn (id, node = document.body) {
        const children = node.children
        for (let i = 0; i < children.length; i++) {
          if (children[i].id === id) findNode = children[i]
          else findNode = fn(id, children[i])
        }
        return findNode
      }
    })(null)
    findId('header') // <div id="header"></div>
    findId('header1') // null
    

    验证通过。

    如何跳出递归函数,不再执行???

    真的完美吗? 非也非也。

    其实有个问题,你有没有发现这两个解法,都是会遍历所有节点,然后再返回节点?看下图,虽然一开始找到了header节点,但是都把剩余的节点都遍历了,然后都打印出来了

    如何跳出递归函数,不再执行???

    而且解法2还有个问题,正常如果有两个id一样的节点,应该是返回第一个。这里是返回的第二个。

    如何跳出递归函数,不再执行???

    总结以上问题:

    • 不必要的遍历
    • id相同的元素应该找到第一个

    正常的做法应该是如果找到对应的节点后,就应该把跳出递归函数,不应该再循环了,这样子就可以解决以上两个问题。

    如何跳出递归函数,不再执行???

    跳出递归

    那递归函数如何跳出?

    break可以吗???

    大家知道break是在for循环,while循环中使用,恰巧我们函数有用了for循环,但是我们函数不是普通的函数,它是递归函数,函数在内部调用自己,所以它会嵌套很多层函数,你break只对那一层函数有用,其他层函数还会照样执行。所以要是要有效果,必须得在最顶层break。这样break才能break掉整个函数。

    大家先看看代码:

    let findId = (function (findNode) {
      return function fn (id, node = document.body) {
        const children = node.children
        for (let i = 0; i < children.length; i++) {
          if (children[i].id === id) findNode = children[i]
          if (findNode = fn(id, children[i])) break     
        }
        return findNode
      }
    })(null)
    findId('content-child')
    

    针对解法2,我们做了一些改进, 当我们利用递归找到findNode的时候,我们直接break,注意此时我们的break是在最顶层,这样子函数就直接执行return了,不会执行后面的函数了。

    效果如下:

    如何跳出递归函数,不再执行???

    当然我们还可以把break改成使用return

    let findId = (function (findNode) {
      return function fn (id, node = document.body) {
        const children = node.children
        for (let i = 0; i < children.length; i++) {
          if (children[i].id === id) return children[i]
          if (findNode = fn(id, children[i])) return findNode
        }
      }
    })(null)
    findId('content-child')
    

    这里原理类似,只要找到findNode,就直接在顶层return。函数就不会执行后面的了。

    还有其它解法吗?

    有。

    当我们找到节点的时候,使用抛出异常,来中止递归函数。

    以下是代码部分:

    let findNode = null
    try {
      function findId (id, node = document.body) {
        const children = node.children
        for (let i = 0; i < children.length; i++) {
          if (children[i].id === id) {
            findNode = children[i]
            throw new Error()
          }
          findId(id, children[i])
        }
      }
      findId('content-child')
    } catch (error) {
      console.log(findNode)
    }
    

    不过我不是很推荐这种方式,因为需要在外面定义一个变量,然后还要写try-catch。

    总结

    以上就是自己总结的如何跳出递归函数,一般有三种方法可以实现:

    • break
    • return
    • 抛出异常(不推荐)

    大家如果有自己的见解和看法,欢迎在评论区一起交流沟通。

    如何跳出递归函数,不再执行???


    起源地下载网 » 如何跳出递归函数,不再执行???

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元