рекурсия не работает без объявления глобальной переменной

почему версия A работает, а версия B-нет? Как я могу заставить версию B работать без объявления глобальной переменной вне функции (что является плохой практикой)? Я не понимаю, почему я не могу просто объявить count внутри самой функции.

A)

  var count = 0;

  var containsFiveOrMoreDivs = function(domElement) {

    if (domElement && domElement.tagName === "DIV") {
      count++;
    }


    //base case: 

    if (count >= 5) {
      return true;
    } else {
      if (domElement.hasChildNodes()) {
        var children = domElement.childNodes;
        for (var i = 0; i < children.length; i++) {

          if (containsFiveOrMoreDivs(children[i])) {
            return true;
          }

        }
      }
      return false;
    }
  };

B)

 var containsFiveOrMoreDivs = function(domElement) {
    var count = 0;
    if (domElement && domElement.tagName === "DIV") {
      count++;
    }


    //base case: 

    if (count >= 5) {
      return true;
    } else {
      if (domElement.hasChildNodes()) {
        var children = domElement.childNodes;
        for (var i = 0; i < children.length; i++) {

          if (containsFiveOrMoreDivs(children[i])) {
            return true;
          }

        }
      }
      return false;
    }
  };

5 ответов


что вам действительно нужно два функции, одна внутри другой:

function containsFiveOrMoreDivs(domElement) {
  var count = 0;
  function doCount(domElement) {
      if (domElement && domElement.tagName === "DIV") {
        count++;
      }

      //base case: 

      if (count >= 5) {
        return true;
      }
      else {
        if (domElement.hasChildNodes()) {
          var children = domElement.childNodes;
          for (var i = 0; i < children.length; i++) {

            if (doCount(children[i])) {
              return true;
            }

          }
        }
        return false;
      }
   }
   return doCount(domElement);
}

В что настройки, вы передаете ссылку на элемент, а затем внешняя функция вызывает внутреннюю функцию после инициализации счетчика.


оригинал не очень хороший ответ здесь

ваша вторая версия ("B") имеет" count " как локальная переменная функции. Каждый вызов функции получает свой собственный " count" переменная, и в каждом вызове первое, что происходит, - это инициализация до нуля.

Если вы не хотите, глобальный, вы можете использовать замыкание:

 var containsFiveOrMoreDivs = function() {
    var count = 0;
    return function(domElement) {
      if (domElement && domElement.tagName === "DIV") {
        count++;
      }

      //base case: 

      if (count >= 5) {
        return true;
      } else {
        if (domElement.hasChildNodes()) {
          var children = domElement.childNodes;
          for (var i = 0; i < children.length; i++) {

            if (containsFiveOrMoreDivs(children[i])) {
              return true;
            }

          }
        }
        return false;
      }
    };
  }();

этот код обертывает вашу фактическую функцию счетчика в анонимную функцию, которая включает переменную "count". Он не будет глобальным; он будет полностью приватным для функции "containsFiveOrMoreDivs". Это похоже на лучшее из обоих миров: вы можете рассматривать "count" как глобальный, но это не глобальный. Вам не нужно беспокоиться о проведении параметра вокруг.


переменные в Javascript существуют в области функций. Каждый раз, когда вы вызываете containsFiveOrMoreDivs, count всегда будет 0 в вашей версии B. следовательно, бесконечная рекурсия.

что вы можете сделать, однако, это передать "count" каждый раз, когда вы вызываете из функции, и использовать это (гарантируя, что он правильно инициализирован в первый раз):

var containsFiveOrMoreDivs = function(domElement, count) {
  if (!count) {
    count=0;
  }
  if (domElement && domElement.tagName === "DIV") {
    count++;
  }


  //base case: 

  if (count >= 5) {
    return true;
  } else {
    if (domElement.hasChildNodes()) {
      var children = domElement.childNodes;
      for (var i = 0; i < children.length; i++) {

        if (containsFiveOrMoreDivs(children[i], count)) {
          return true;
        }

      }
    }
    return false;
  }
};

назовите это так же, как вы в настоящее время (containsFiveOrMoreDivs('elementname');)


версия B не будет работать, потому что каждый раз, когда функция называется counter объявлено заново, так что counter никогда не увеличивается.


ваша рекурсивная функция должна использовать count в качестве аргумента. Кстати у вас это будет инициализировать счетчик до 0 не важно, сколько раз вам повторить.

вот пример рекурсивной функции, которая потребляет" количество раз, чтобы что-то сделать " в качестве параметра. Изменить его, чтобы поддержать ваше дело. Ваш базовый случай будет чем-то вроде "count больше 5", и каждый раз, когда вы вызываете рекурсивно, вы добавляете 1 к счету, который вы предоставляете рекурсивному вызову.

function executeMany(fn, count) {
    if (count > 0) {
        fn();
        executeMany(fn, count - 1)
    }
}

// this logs "Test" to the console twice
executeMany(function() { console.log("Test"); }, 2);

вы можете определить функцию с count параметр и передайте начальное значение или если вы используете ECMA 16, вы можете установить значение по умолчанию для параметра, выполнив count=0.

var containsFiveOrMoreDivs = function(domElement, count) {
    if (domElement && domElement.tagName === "DIV") {
      count++;
    }


    //base case: 

    if (count >= 5) {
      return true;
    } else {
      if (domElement.hasChildNodes()) {
        var children = domElement.childNodes;
        for (var i = 0; i < children.length; i++) {

          if (containsFiveOrMoreDivs(children[i]), count) {
            return true;
          }

        }
      }
      return false;
    }
  };

// call function and set counter to some initial value, such as zero
containsFiveOrMoreDivs(domElement, 0);