2015-08-30 26 views
6

Perché la versione A funziona ma la versione B no? Come posso far funzionare la versione B senza dichiarare una variabile globale al di fuori della funzione (che è una cattiva pratica)? Non sono chiaro sul motivo per cui non posso semplicemente dichiarare il conteggio all'interno della funzione stessa.la ricorsione non funziona senza dichiarare la variabile globale

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; 
    } 
    }; 
+3

Per quello che vale, il tuo problema non ha nulla a che fare con "passa per riferimento", che comunque non è possibile in JavaScript. – Pointy

+0

C'è qualche ragione per cui non stai facendo 'element.querySelectorAll ('div'). Length> 5'? –

risposta

6

Quello che occorre è due funzioni, uno dentro l'altro:

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); 
} 

In tale configurazione, si passa un riferimento elemento, e quindi la funzione esterna chiama la funzione interna dopo l'inizializzazione del contatore .


originale non molto buona risposta qui

tua seconda versione ("B") ha "contare" come variabile locale della funzione. Ogni invocazione della funzione ottiene la propria variabile "conteggio", e in ogni invocazione la prima cosa che accade è che è inizializzato a zero.

Se non si desidera un globale, è possibile utilizzare una chiusura:

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; 
     } 
    }; 
    }(); 

Quel codice avvolge la funzione attuale contatore in una funzione anonima che include la variabile "contare". Non sarà globale; sarà completamente privato alla funzione "containsFiveOrMoreDivs". Questo è come il meglio di entrambi i mondi: puoi considerare "conta" come globale, ma è non globale. Non devi preoccuparti di portare un parametro in giro.

2

versione B non funzionerà perché ogni volta che la funzione viene chiamata la counter è ridichiarato, in modo counter non incrementa.

+0

Grazie, ha senso! Un suggerimento era di fare contare un parametro. Se lo faccio, come posso gestirlo all'interno della funzione? – devdropper87

+1

Vedere la risposta di @fullstacks di seguito sulla dichiarazione di un valore predefinito per il parametro count. Questa sarebbe la soluzione migliore se si desidera evitare di dichiarare qualsiasi variabile esterna. –

1

La funzione ricorsiva deve utilizzare il conteggio come argomento. Il modo in cui lo hai inizializzato conta fino a 0, non importa quante volte ti ricorri.

Ecco un esempio di una funzione ricorsiva che consuma "il numero di volte per fare qualcosa" come parametro. Modificalo per supportare il tuo caso. Il tuo caso base sarebbe qualcosa come "il conteggio è maggiore di 5" e ogni volta che chiami in modo ricorsivo, aggiungi 1 al conteggio che fornisci alla chiamata ricorsiva.

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); 
+0

grazie! se dichiaro il conteggio come parametro, come lo gestisco all'interno del corpo della funzione? – devdropper87

3

Le variabili in Javascript esistono nello scope funzione. Ogni volta che chiami containsFiveOrMoreDivs, count sarà sempre 0 nella tua versione B. Quindi, ricorsione infinita.

Cosa si può fare, però, è passa in 'conteggio' ogni volta che si chiama all'interno della funzione, e l'uso che (assicurando che è stato inizializzato correttamente la prima volta):

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; 
    } 
}; 

Chiamatela come ci si trova (containsFiveOrMoreDivs('elementname');)

+0

che non funziona, appena testato sulla console su about: blank dopo aver aggiunto 5 div! restituisce falso. – devdropper87

+0

Buon punto: funzionerà se i div sono nidificati uno sotto l'altro, ma non contano i fratelli! Questo problema si applica anche alla soluzione di @FullStack di seguito. @Pointy - la tua soluzione sembra abbastanza elegante, ma ogni volta che chiami 'containsFiveOrMoreDivs (children [i])' all'interno della funzione restituita, stai restituendo una nuova chiusura *** *** che ha una nuova variabile di conteggio di valore 0. – oflahero

1

si potrebbe definire la funzione con un parametro count e superare un valore iniziale o se si utilizza un ECMA 16 è possibile impostare un valore predefinito per il parametro facendo 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); 
+1

Questo era esattamente quello che stavo per dire a @freezycold. Ottima soluzione. –

+0

Questo è grande e funzionerà in Firefox e nessun altro browser attuale. – Pointy

Problemi correlati