Chiamando una funzione javascript in modo ricorsivo

Posso creare una funzione ricorsiva in una variabile in questo modo:

/* Count down to 0 recursively. */ var functionHolder = function (counter) { output(counter); if (counter > 0) { functionHolder(counter-1); } } 

Con questo, functionHolder(3); emetterebbe 3 2 1 0 . Diciamo che ho fatto quanto segue:

 var copyFunction = functionHolder; 

copyFunction(3); emetterebbe 3 2 1 0 come sopra. Se poi ho cambiato functionHolder come segue:

 functionHolder = function(whatever) { output("Stop counting!"); 

Quindi functionHolder(3); darebbe Stop counting! , come previsto.

copyFunction(3); ora dà 3 Stop counting! in quanto si riferisce a functionHolder , non alla funzione (che essa stessa indica). Ciò potrebbe essere desiderabile in alcune circostanze, ma esiste un modo per scrivere la funzione in modo che si definisca piuttosto che la variabile che la detiene?

    Cioè, è ansible cambiare solo la functionHolder(counter-1); così che passare tutti questi passaggi dà ancora 3 2 1 0 quando chiamiamo copyFunction(3); ? Ho provato this(counter-1); ma questo mi dà l’errore che this is not a function .

    Utilizzo di espressioni di funzioni denominate:

    Puoi dare un’espressione di funzione un nome che è in realtà privato ed è visibile solo dall’interno della funzione ifself:

     var factorial = function myself (n) { if (n <= 1) { return 1; } return n * myself(n-1); } typeof myself === 'undefined' 

    Qui myself è visibile solo all'interno della funzione stessa.

    È ansible utilizzare questo nome privato per chiamare la funzione in modo ricorsivo.

    Vedi 13. Function Definition della specifica ECMAScript 5:

    L'identificatore in una FunctionExpression può essere fatto riferimento all'interno del FunctionBody di FunctionExpression per consentire alla funzione di chiamarsi in modo ricorsivo. Tuttavia, a differenza di una FunctionDeclaration, non è ansible fare riferimento a Identifier in FunctionExpression e non influisce sull'ambito che racchiude FunctionExpression.

    Si noti che Internet Explorer fino alla versione 8 non si comporta correttamente poiché il nome è effettivamente visibile nell'ambiente variabile allegato e fa riferimento a un duplicato della funzione effettiva (vedere il commento di patrick dw sotto).

    Utilizzando arguments.callee:

    In alternativa è ansible utilizzare arguments.callee per fare riferimento alla funzione corrente:

     var factorial = function (n) { if (n <= 1) { return 1; } return n * arguments.callee(n-1); } 

    La quinta edizione di ECMAScript vieta l'uso di arguments.callee () in modalità rigorosa , tuttavia:

    (Da MDN ): in argomenti di codice normali. Call si riferisce alla funzione di inclusione. Questo caso d'uso è debole: basta nominare la funzione di inclusione! Inoltre, arguments.callee sostanzialmente ostacola le ottimizzazioni come le funzioni di inlining, perché deve essere reso ansible fornire un riferimento alla funzione non -lined se si accede a arguments.callee. arguments.callee per le funzioni in modalità rigorosa è una proprietà non cancellabile che genera quando viene impostata o recuperata.

    Puoi accedere alla funzione stessa usando arguments.callee [MDN] :

     if (counter>0) { arguments.callee(counter-1); } 

    Questo si interromperà in modalità rigorosa, tuttavia.

    So che questa è una vecchia domanda, ma ho pensato di presentare un’altra soluzione che potrebbe essere utilizzata se si desidera evitare l’uso di espressioni di funzioni con nome. (Non dicendo che dovresti o non dovresti evitarlo, solo presentando un’altra soluzione)

      var fn = (function() { var innerFn = function(counter) { console.log(counter); if(counter > 0) { innerFn(counter-1); } }; return innerFn; })(); console.log("running fn"); fn(3); var copyFn = fn; console.log("running copyFn"); copyFn(3); fn = function() { console.log("done"); }; console.log("fn after reassignment"); fn(3); console.log("copyFn after reassignment of fn"); copyFn(3); 

    Puoi usare il combinatore Y: ( Wikipedia )

     // ES5 syntax var Y = function Y(a) { return (function (a) { return a(a); })(function (b) { return a(function (a) { return b(b)(a); }); }); }; // ES6 syntax const Y = a=>(a=>a(a))(b=>a(a=>b(b)(a))); // If the function accepts more than one parameter: const Y = a=>(a=>a(a))(b=>a((...a)=>b(b)(...a))); 

    E puoi usarlo come questo:

     // ES5 var fn = Y(function(fn) { return function(counter) { console.log(counter); if (counter > 0) { fn(counter - 1); } } }); // ES6 const fn = Y(fn => counter => { console.log(counter); if (counter > 0) { fn(counter - 1); } }); 

    Ecco un esempio molto semplice:

     var counter = 0; function getSlug(tokens) { var slug = ''; if (!!tokens.length) { slug = tokens.shift(); slug = slug.toLowerCase(); slug += getSlug(tokens); counter += 1; console.log('THE SLUG ELEMENT IS: %s, counter is: %s', slug, counter); } return slug; } var mySlug = getSlug(['This', 'Is', 'My', 'Slug']); console.log('THE SLUG IS: %s', mySlug); 

    Si noti che il counter conta “all’indietro” rispetto al valore di slug . Ciò è dovuto alla posizione in cui stiamo registrando questi valori, in quanto la funzione ricorre prima della registrazione, quindi, in pratica, continuiamo a nidificare sempre più in profondità nello stack delle chiamate prima che avvenga la registrazione.

    Una volta che la ricorsività incontra l’ultimo elemento call-stack, esegue il trampolino “out” delle chiamate di funzione, mentre il primo incremento del counter verifica all’interno dell’ultima chiamata nidificata.

    So che questa non è una “correzione” sul codice dell’Indagine, ma dato il titolo ho pensato di esemplificare genericamente la Ricorsione per una migliore comprensione della ricorsione, a titolo definitivo.