2016-06-21 6 views
1

Ich bin an einem Problem arbeiten, die eine rekursive Funktion und haben festgestellt, erfordert, dass verschachtelte Ausführungen der Eltern Parameter modifizieren zu sein scheinen:Haben rekursive Funktionen spezielle Scoping-Regeln?

var foo = function(ar) { 
    console.log('Calling function - Array: ' + ar.toString()); 
    if(ar.length > 1){ 
    var ele = ar.pop(); 
    foo(ar); 
    console.log('Function recursion ends - Array: ' + ar.toString() + ' Popped: ' + ele); 
    return; 
    } else { 
    console.log('Function ends - Array: ' + ar.toString()); 
    return; 
    } 
} 

foo([1,2,3]); 

Ausgänge (Einzug von mir):

/* 
Calling function - Array: 1,2,3 
    Calling function - Array: 1,2 
    Calling function - Array: 1 
    Function ends - Array: 1 
    Function recursion ends - Array: 1 Popped: 2 
Function recursion ends - Array: 1 Popped: 3 <-- What happened to 2? 
*/ 

Das scheint seltsam - weil ich die Funktion mit [1,2,3] aufgerufen habe und ich würde erwarten, dass die erste Iteration der Funktion alle Elemente beibehalten, die zwischenbereitgestellt werdenund ele - aber stattdessen, wenn die Funktion endet, bleibt nur 1 im bereitgestellten Array - was ist mit 2 passiert? Hat die verschachtelte Ausführung pop es aus der Variablen der ersten Ausführung?

Mein Verständnis von Funktionsumfang in JavaScript würde sagen, dass an eine Funktion übergeben Variablen nur lokal ändern können und sie nicht zurück zu den global/Eltern Umfang exportieren wie hier zu sehen:

var bar = 'bar'; 

function doBar(bar){ 
bar = 'foo'; 
} 

doBar(bar); 
console.log(bar); //Outputs 'bar'; 

Aber der Ausgang von der rekursiven Funktion scheint dieses Verständnis herauszufordern.

Wie kann ich verhindern, dass diese verschachtelten Ausführungen den Parent-Parameter ändern, um die fehlende 2 zurück zu bringen? Ist mein Verständnis von Scoping in JavaScript falsch?


In meinem erbärmlichen Versuch an Strohhalme zu erfassen, bevor diese Frage zu öffnen, habe ich versucht, die Funktion innerhalb einer Schließung Ausführung:

var foo = function(ar) { 
    console.log('Calling function - Array: ' + ar.toString()); 
    if(ar.length > 1){ 
    var ele = ar.pop(); 
    (function(foo, ar){ 
     foo(ar); 
    })(foo, ar) 

    console.log('Function recursion ends - Array: ' + ar.toString() + ' Popped: ' + ele); 
    return; 
    } else { 
    console.log('Function ends - Array: ' + ar.toString()); 
    return; 
    } 
} 

Aber ich habe die gleichen Ergebnisse wie ohne den Verschluss mit - Ich vermute, weil ich explizit in ar und foo übergeben habe es nicht anders als ohne die Schließung.

+0

Nested foo() auf Array ausgeführt, bevor Sie etwas anmelden. Versuchen Sie, vor dem verschachtelten 'foo()' zu loggen. – charlietfl

+3

Nein, die Rekursion hat nichts besonderes zu tun, und das hat nichts mit Scope zu tun. Jeder Aufruf erzeugt eine eigene 'ar'-Variable. Sie verweisen nur auf das gleiche Array-Objekt und "pop" mutiert es. – Bergi

+0

Nach dieser Zeit beginnen Sie zu loggen 'Funktion Rekursion endet ...' Was ist "ist" gegeben, was die Konsole Ihnen sagt? – JonSG

Antwort

1

Array.prototype.pop() ist eine veränderbare Operation. Wenn Sie foo rekursiv aufrufen, ist diese änderbare Operation weiterhin im aufrufenden Bereich wirksam. Es gibt keine seltsamen Dinge mit Umfang, es ist nur Sie erwarten vielleicht, dass die Operationen innerhalb foo kann nicht die Interna der Parameter ändern Sie geben es. Arrays werden als Referenz übergeben, was bedeutet, dass die Parameter auf dasselbe Array verweisen, das der aufrufende Bereich verwendet.

Stattdessen können Sie arr.slice(0, -1) anrufen. Dadurch wird eine flache Kopie des Arrays zurückgegeben, anstatt das vorhandene Array zu ändern. Es wird ändern, wie Sie den letzten Index des Arrays erhalten.

var foo = function(ar) { 
 
    console.log('Calling function - Array: ' + ar.toString()); 
 
    if(ar.length > 1){ 
 
    var ar2 = ar.slice(0, -1); 
 
    foo(ar2); 
 
    console.log('Function recursion ends - Array: ' + ar2.toString() + ' Popped: ' + ar.slice(-1)); 
 
    return; 
 
    } else { 
 
    console.log('Function ends - Array: ' + ar.toString()); 
 
    return; 
 
    } 
 
} 
 

 
foo([1,2,3]);

+2

Sie haben die Erwartung des OP nicht angesprochen, dass "Variablen, die an eine Funktion übergeben werden, diese nur lokal modifizieren können". Die Antwort sollte explizit erwähnen, dass das Argument "ar" jedes Aufrufs auf dasselbe Array verweist. Wie direkt unter der Frage kommentiert, geht es hier wirklich nicht um Rekursion oder Scope, sondern um Array-Referenzen, die keine Kopien des Arrays erstellen. – nnnnnn

+0

"Sie erwarten vielleicht, dass die Operationen in' foo' die Parameter, die Sie angeben, nicht ändern können. " Boom. Genau da. Das Entfernen des letzten Elements des Arrays war jedoch ein beabsichtigter Teil der Funktionsweise der Funktion. Es war also eine bessere Lösung, das Array zu kopieren, wenn die Frage als Dupe markiert wurde. – HPierce

+0

@nnnnnn Ich habe darauf angespielt, aber jetzt habe ich das explizit in der Antwort gestellt. Ich bin froh, dass es die Frage trotzdem ausreichend beantwortet hat. – 4castle

0

Wie @ 4castle schon sagt, wenn Sie bei jeder Iteration eine haltbarere Ansicht des Arrays haben wollen, Sie ar mit Slice klonen(). Alternativ können Sie möglicherweise auch Ihre Funktion foo() überarbeiten, um mehr Platz für die Arbeit mit dem "aktuellen" ar und ele zu haben.

var foo = function(ar) { 
 
    console.log('Given Array: ' + ar.toString()); 
 
    
 
    if(ar.length === 0){ 
 
    console.log('recursion ended'); 
 
    return; 
 
    } 
 
    
 
    var ele = ar.pop(); 
 
    console.log('Array Now: ' + ar.toString() + ' After Popping: ' + ele); 
 
    console.log(" "); 
 

 
    foo(ar); 
 
} 
 

 
foo([1,2,3]);