Hugo: Warum ist die Variable nicht im Scope (und wie greife ich dann auf sie zu)?

Ok, bitte nicht erschlagen, für das was jetzt folgt :/

...ich habe den Code auf das Wesentliche reduziert, wer das Original sehen will, bitte melden, dazu empfehle ich aber starke Kopfschmerztabletten.

Prinzipiell soll der ganze Spuk einem modularen funktionellen Aufbau dienen, soll heißen, die Funktionen sollen unabhängig voneinander aufrufbar sein (so wird leiderNochEineFunktion z.B. im Original noch an anderen Stellen verwendet):


function ersteFunktion(eingabe_aussen) {
	return function(eingabe_innen) {
		let zaehler = 0;
		leiderNochEineFunktion(eingabe_aussen)(eingabe_innen);

	}
}

function leiderNochEineFunktion(zweite_aussen) {
	return function(zweite_innen) {
		zaehler++;
		console.log(`${zweite_aussen} ${zweite_innen} ${zaehler}`);

	}
}

ersteFunktion("Erste Iteration")("Zweite Iteration");

// UNCAUGHT REFERENCE ERROR: zaehler IS NOT DEFINED

Wie kann ich auf Zaehler zugreifen?

...ich will ja auf die Originalvariable zugreifen, daher kann ich ihn nicht einfach als Argument übergeben.

Eigentlich müsste zaehler ja auch im Scope sein, so wie ich das sehe:

(...) >> let zaehler >> leiderNochEineFunktion >> function >> Zugriff auf zaehler

Danke für eure Hilfe!

(zaehler als globale Variable funktioniert witzigerweise ... aber kann ja auch nicht im Sinne des Erfinders sein, wenn ich den globalen Namespace zumülle…)

  1. Hallo Hugo,

    du möchtest mehr über den Unterschied zwischen statischem und dynamischen Scope lernen.

    Bei statischem bzw. lexikalischem Scope sind in einer Funktion nur die Variablen sichtbar, die in ihrer lexikalischen Umgebung definiert wurden, also in den umgebenden Gültigkeitsbereichen. Es ist also schon vor der Laufzeit nur durch Ansicht des Quellcodes zu erkennen, welche Variable wo sichtbar ist.

    let x = 3;
    
    function f() {
        console.log(x + y);
    }
    
    function g() {
        let y = 2;
    
        function h() {
            console.log(x + y);
        }
    
        f();
        h();
    }
    
    g();
    

    Die globale Variable x ist in der lexikalischen Umgebung sowohl der Funktion f als auch der Funktionen g und h und deshalb in allen Funktionen sichtbar.

    Die Variable y ist hingegen nur innerhalb der Funktion g sichtbar und in h, weil der lokale Gültigkeitsbereich von g die Funktion h umgibt. Innerhalb von f ist die Variable hingegen nicht sichtbar, da die Umgebung aus der Sicht von f nur aus dem globalen Gültigkeitsbereich besteht.

    Dass f aus g heraus aufgerufen wird ist nicht von Belang. Bei lexikalischem Scope kommt es nur auf den Ort der Definition einer Funktion an, nicht auf den Ort an dem sie aufgerufen wird.

    Bei dynamischem Scope ist das anders. Hier spielt es für die Sichtbarkeit von Variablen tatsächlich eine Rolle, welche Funktionen in welcher Reihenfolge aufgerufen wurden.

    my $*x = 10;
    
    sub f {
      my $*x = 20;
      g();
    }
    
    sub g {
      say $*x += 10;
    }
    

    Das Perl-Beispiel habe ich aus diesem Beitrag entnommen.

    Auch hier wird eine globale und eine lokale Variable definiert, so wie zwei Funktionen im globalen Scope. Würden hier die Regeln für lexikalischen Scope gelten, dann müsste die Funktion g die globale Variable x referenzieren und nach der Addition 20 ausgeben.

    Hier gelten jedoch die Regeln für dynamischen Scope, weshalb bei der Auflösung des Bezeichners x in der Funktion g nicht zuerst in der lexikalischen Umgebung, also dem globalen Scope, sondern im lokalen Gültigkeitsbereich der aufrufenden Funktion f gesucht wird.

    Dort wird eine Variable x gefunden und deshalb der Wert 30 ausgegeben.

    JavaScript verwendet lexikalischen Scope. Deshalb ist die Variable zaehler nur innerhalb der anonymen Funktion sichtbar, die von ersteFunktion zurückgegeben wird.

    Viele Grüße

  2. Hallo Hugo,

    sicherlich ist der Code, den du zeigst, abgestrippt und dein Programm ist komplexer.

    Um auch nur ansatzweise klarstellen zu können, wo zaehler hingehört, muss man aber mehr wissen. Diese Variable kann global sein, lokal in ersteFunktion oder lokal in leiderNochEineFunktion. Eine Deklaration in der inneren Funktion von ersteFunktion erscheint mir definitiv sinnlos, das ist viel zu weit innen.

    Aber, je nach dem, wo sie deklariert wird, zählst Du ganz unterschiedliche Dinge. Denn Du wirfst hier wie wild mit Closures um Dich, und der Ort des Zählers bestimmt, wieviele Instanzen dieses Zählers du erzeugst.

    Eine globale Variable muss nicht immer schlecht sein. Wenn Du exakt einen Zähler haben willst, wird es auch keine andere Möglichkeit geben. Wenn Du vermeiden willst, den global scope vollzumüllen, lege alles was Du das hast in ein Modul (ECMAScript Modul oder IIFE).

    Rolf

    --
    sumpsi - posui - obstruxi
  3. Lieber Hugo,

    Ok, bitte nicht erschlagen, für das was jetzt folgt :/

    wir waren alle irgendwann einmal Anfänger.

    ...ich habe den Code auf das Wesentliche reduziert, wer das Original sehen will, bitte melden, dazu empfehle ich aber starke Kopfschmerztabletten.

    Mir wäre es lieber, wenn Du erklären könntest, was Du eigentlich erreichen möchtest. Dein Code hat ja ein Ziel. Das Ziel ist nicht, innerhalb einer Funktion Zugriff auf eine Variable zu haben, sondern etwas ganz bestimmtes an Funktionalität zu erreichen. Also Butter bei die Fische: Was genau möchtest Du erreichen? Dann kannst Du einen Link zum Original beifügen und unsereiner hier kann dann verstehen, was das Ganze soll, um Dir im Gegenzug wirklich sinnvolle Hinweise zu einer möglichen Lösung zu geben.

    Liebe Grüße

    Felix Riesterer