Рекурсивный JavaScript возвращается после первого прохода

У меня есть следующая рекурсивная функция javascript, которая зацикливается на дочерних элементах backbone.marionette CollectionView, у которых есть дочерние элементы ItemView, которые, в свою очередь, являются CollectionViews:

  findViewByCid: function(cid, children){
      var col = (arguments.length === 1) ? this.children : children;

      if(cid in col){
        return col[cid];
      }

      for(child in col){
        var grandChildren = col[child].children;

        if(cid in grandChildren){
          return grandChildren[cid];
        }

        if(grandChildren && (!jQuery.isEmptyObject(grandChildren))){
          return this.findViewByCid(cid, grandChildren);
        }
      }
    }

Я вызываю это так:

var view = DocumentManager.Documents.treeRoot.findViewByCid(model.cid);

Проблема в строке:

return this.findViewByCid(cid, grandChildren);

Если у меня есть такая иерархия

c1
|_c2
  |_c3
|_c4
  |_c5

Тогда оператор return приведет к выходу функции после прохождения узла th3 c2 и никогда не дойдет до c4 и т. д.

Если я удалю оператор return, будет найден правильный дочерний элемент, но будет возвращено значение null.

Как я могу продолжить анализ иерархии и вернуть значение?


person dagda1    schedule 21.08.2012    source источник


Ответы (5)


return закроет вашу функцию.

Попробуйте сохранить все в var и вернуть в самом конце, это может быть массив, если вам нужно вернуть более одного значения. (и не объявляйте переменные в цикле for!)

Вот предложение :

findViewByCid: function(cid, children){
  var willBeReturned=[];
  var grandChildren;

  var col = (arguments.length === 1) ? this.children : children;

  if(cid in col){
    willBeReturned[willBeReturned.length] = col[cid];
  }
  for(child in col){
    grandChildren = col[child].children;

    if(cid in grandChildren){
      willBeReturned[willBeReturned.length] = grandChildren[cid];
    }

    if(grandChildren && (!jQuery.isEmptyObject(grandChildren))){
      willBeReturned[willBeReturned.length] = this.findViewByCid(cid, grandChildren);
    }
  }
  return willBeReturned;
}
person Gil Zumbrunnen    schedule 21.08.2012

Вам нужно будет вернуться, только если что-то было найдено, иначе оператор return разорвет ваш цикл без поиска других дочерних элементов. Это простой поиск в глубину, что вам нужно.

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

findViewByCid: function(cid) {
    var col = this.children;
    if (!col) // break if the node has no children
        return false;
    if (cid in col) // look for cid and return the node if one found
        return col[cid];
    for (var child in col) {
        // search through each child and return the result if something is found
        var found = col[child].findViewByCid(cid);
        if (found)
            return found;
    }
    // else nothing was found
    return false;
}

Или иметь функцию, принимающую узел в качестве аргумента:

function findViewByCid(cid, node) {
    var col = node.children;
    if (!col)
        return false;
    if (cid in col)
        return col[cid];
    for (var child in col) {
        var found = findViewByCid(cid, col[child]);
        if (found)
            return found;
    }
    return false;
}

Однако похоже, что этот алгоритм не сможет найти корневой узел. Было бы лучше, если бы вы могли идентифицировать текущий узел по cid вместо того, чтобы смотреть на все его дочерние элементы:

if (this /*… is what we have searched for */)
    return this;
person Bergi    schedule 21.08.2012

findViewByCid: function(cid, children) {
    var col = (arguments.length === 1) ? this.children : children;

    if(cid in col){
        return col[cid];
    }       
    for(var childKey in col) {
        var grandChildren = col[childKey].children,
            childView;

        if (grandChildren) {
            childView = this.findViewByCid(cid, grandChildren);
        }

        if (childView) {
            return childView;
        }
    }
    return null;
}

Прежде всего, это похоже на Backbone.js, может быть полезно пометить его, если это так. Я чувствую, что люди, возможно, боролись с подобными проблемами и знают лучшие способы хранения ссылок на представления.

Вы хотите вернуть что-то только в том случае, если оно было найдено... простое использование возврата при первом рекурсивном вызове заставит метод остановить выполнение при поиске первого набора внуков, даже если ничего не было найдено.

Я бы также добавил var перед новой переменной, которую вы вводите в свой цикл for — без нее переменная будет глобальной.

person terranmoccasin    schedule 21.08.2012
comment
Это backbone.marionette, и я перебираю CollectionView с ItemView, который является CollectionView, что делает его немного более сложным, чем обычно. - person dagda1; 21.08.2012

Вот что у меня получилось, и это backbone.marionette, перебирающий collectionView с itemView CollectionView:

findViewByCid: function(cid){
  var self = this,
      ret;

  function findView(cid, children){
    var col = (arguments.length === 1) ? self.children : children,
    grandChildren;

    if(cid in col){
      ret = col[cid];
    }

    for(child in col){
      grandChildren = col[child].children;

      if(cid in grandChildren){
        ret = grandChildren[cid];
      }

      if(grandChildren && (!jQuery.isEmptyObject(grandChildren))){
        findView(cid, grandChildren);
      }
    }
  };

  findView(cid);

  return ret;
}
person dagda1    schedule 21.08.2012

Я считаю, что строка if (cid in col) не то, что вы хотите сделать. Пытаться

findViewByCid: function(cid, children){
  if (this.cid === cid) return this;

  var col = (arguments.length === 1) ? this.children : children;
  for(var childI in col){
    var child = col[childI];

    if (child.cid === cid) {
      return child;
    }

    var grandChildren = child.children;
    if(grandChildren && (!jQuery.isEmptyObject(grandChildren))){
      return this.findViewByCid(cid, grandChildren);
    }
  }
}
person metadings    schedule 21.08.2012
comment
Трудно объяснить. учтите, что cid равен 5. cid в модели проверяет, есть ли модель [5], что не совпадает с моделью ['cid'] === 5 или model.cid === 5. - person metadings; 21.08.2012