JavaScript/Variable

Aus SELFHTML-Wiki
Wechseln zu: Navigation, Suche

Eine Variable lässt sich am besten mit einem Behälter, der genau einen Wert aufnehmen kann, vergleichen. Der Behälter ist ein von JavaScript verwalteter Speicherplatz mit einem (unveränderlichen) Namen. In einem solchen Speicherplatz können Sie beliebige Werte ablegen und später wieder verwenden. Sie können einen vorhandenen Wert auch beliebig oft durch andere Werte ersetzen. Die Möglichkeit, Werte zu errechnen, in Variablen zu speichern und weiterzugeben, gibt Programmen ihre Flexibilität.

Bei einem solchen Wert kann es sich beispielsweise um eine Zahl handeln. JavaScript kennt unterschiedliche Wertetypen, auch solche, die sich aus anderen Werten zusammensetzen. Im nächsten Artikel dieser Reihe werden wir das genauer betrachten. An dieser Stelle soll nur erwähnt werden, dass eine Variable jeden der in JavaScript verfügbaren Werte speichern kann und - im Gegensatz zu vielen anderen Sprachen - nicht auf einen bestimmten Wertetyp festgelegt werden muss.

Um eine Variable zu erzeugen, muss sie für JavaScript bekannt gemacht („deklariert“) werden. Mit der Deklaration wird der erforderliche Speicherplatz bereitgestellt und ihm ein Name zugeordnet. Darüber hinaus legt der Ort, an dem sich die Deklaration befindet, den Gültigkeitsbereich der Variablen fest, also den Bereich Ihres Programms, in dem diese Variable sichtbar und verwendbar ist.

Beachten Sie: Die Schreibweise eines Variablennamens ist in JavaScript relevant. Bei wiki und Wiki handelt es sich um unterschiedliche Variablen!

Um den Wert, der in einer Variablen gespeichert ist, wieder auszulesen, schreiben Sie den Variablennamen einfach dorthin, wo Sie andernfalls den Wert hingeschrieben hätten.

Werte in Variablen speichern und lesen
kante = 5;
fläche = kante * kante;

Für dieses Beispiel wurden die Variablen kante und fläche noch nicht deklariert. Wie das geht, zeigen wir gleich. Nehmen Sie einstweilen an, das sei irgendwie geschehen.

In der ersten Zeile wird in der Variablen kante die Zahl 5 gespeichert. Diese Zeile bildet eine so genannte Wertzuweisung, die durch den Zuweisungsoperator = ausgelöst wird. In der zweiten Zeile wird die Variable kante in einer Formel verwendet, um den Wert zu ermitteln, der in der Variablen fläche gespeichert werden soll. Der Rechenoperator * bewirkt eine Multiplikation, der Wert wird als 25 lauten.

Deklaration von Variablen

JavaScript unterscheidet vier Arten, Variablen zu deklarieren. Drei davon erfolgen mit den Schlüsselwörtern var, let und const, die vierte besteht darin, Parameter einer Funktion zu deklarieren (die sich aber so verhalten, als seien sie mit var deklariert). Parameter werden wir zusammen mit Funktionen betrachten.

Beachten Sie: Wenn JavaScript einen Wert aus einer undeklarierten Variable zu lesen versucht, bricht es das Programm mit einem ReferenceError ab. Beim Schreiben auf undeklarierte Variablen kommt es darauf an, ob das Programm im strikten Modus läuft. Dort bricht auch eine Zuweisung das Programm ab. In veralteten Programmen, für die der strikte Modus nicht eingeschaltet wurde, bewirkt eine solche Zuweisung aber automatisch die Deklaration einer Variable, die überall gültig ist. Die Probleme, die daraus entstehen können, werden im Artikel zum strikten Modus aufgeführt.

var

Beachten Sie: Das Schlüsselwort var ist veraltet und sollte in Zukunft nicht mehr verwendet werden. Stattdessen wird empfohlen, Variablen mit dem Schlüsselwort let zu deklarieren. Browser unterstützen weiterhin var aus Kompatibilitätsgründen.

Als JavaScript entstand, gab es nur das Schlüsselwort var, um eine Variable zu deklarieren. Notieren Sie einfach var und listen Sie danach die Variablennamen auf, die Sie verwenden möchten. Wenn Sie mehrere Variablen auf einmal deklarieren wollen, trennen Sie ihre Namen durch ein Komma.

Deklaration mit var
var kante;
var fläche, volumen;

Für die so erzeugten Variablen ist zunächst kein Wert bekannt. Da es aber ungeschickt wäre, in dem bereitgestellten Speicherplatz irgendwelche alten Daten vorzufinden, schreibt JavaScript in neue Variablen automatisch den speziellen Wert undefined hinein - das ist ein JavaScript-Schlüsselwort, das den Wert für leere Variablen repräsentiert.

Außer mit dem Operator zur Wertzuweisung können Sie einer Variable auch sofort bei ihrer Deklaration einen Wert geben. Das nennt man Initialisierung.

Führen wir unsere beiden Beispiele nun zusammen. Wir zeigen die Deklaration einmal ohne und einmal mit Initialisierung.

Variablen: Werte deklarieren, speichern und lesen
var kante;                  // Deklariert die Variable kante, ohne sie zu initialisieren
kante = 5;                  // Speichern der Zahl 5 in kante

var fläche = kante * kante; // Deklariere die Variable fläche und initialisiere sie 
                            // sofort mit dem Ergebnis einer Berechnung

Hebung

Bevor JavaScript ein Programm ausführt, liest es es komplett ein und übersetzt es in einen internen Zwischencode. Bei diesem Schritt registriert es auch sämtliche Deklarationsanweisungen. Variablennamen sind damit automatisch ab dem Beginn des Programmbereichs bekannt, in dem sie deklariert wurden.

Das Besondere an var-Deklarationen ist, dass sie auch sofort zum Beginn dieses Programmbereichs gültig sind. Dieses Konzept heißt Hebung (engl. hoisting). Sie können einer solchen Variablen deshalb bereits einen Wert zuweisen, bevor die Programmzeile mit der var-Deklaration erreicht wurde. Aber Vorsicht, das gilt nur für die Deklaration. Wenn Sie in der Deklaration eine Initialisierung vorgesehen haben, findet sie erst an der Stelle statt, wo die var-Anweisung steht. Bis dahin ist die Variable uninitialisiert.

Hoisting
var x = y;         // y ist hier bereits deklariert, aber noch undefined
var y = 7;         // y wird deklariert und mit 7 initialisiert

Wegen der Hebung aller var-Deklarationen bricht das Programm in Zeile 1 nicht mit der Fehlermeldung ab, dass y undeklariert sei. Aber weil y erst in Zeile 1 initialisiert wird, bekommt x den Wert undefined.

const und let

Die var Anweisung hat zum einen die merkwürdige Eigenschaft des Hebung an den Beginn des Gültigkeitsbereichs, und zum anderen hat sie gewisse Einschränkungen bezüglich der möglichen Gültigkeitsbereiche, auf die wir später eingehen.

In der JavaScript-Sprachversion ECMAScript 2015 (ES6) wurden deshalb die Anweisungen const und let zur Variablendeklaration hinzugefügt. Das Verhalten dieser Anweisungen entspricht eher dem von Sprachen wie Java oder C. Das heißt: die grundsätzliche Funktionalität ist wie bei var, aber die Hebung entfällt. Damit werden Sie beim Programmieren dazu angehalten, Variablen immer erst zu deklarieren, bevor Sie sie verwenden. Die Programmlesbarkeit verbessert sich dadurch deutlich. Die fehlende Hebung ermöglicht es auch, Variablen nur für einen Teilbereich einer Funktion zu deklarieren.

Empfehlung: Verwenden Sie immer const und let statt var und beachten Sie die Empfehlungen für die Praxis.

Die neuen Deklarationsanweisungen unterscheiden zwischen veränderbaren und unveränderbaren Variablen. Unveränderbare Variable - das klingt wie ein Widerspruch. Solche Variablen sind aber nicht nutzlos. Man benötigt oft Speicherplätze, um ein Zwischenergebnis zu speichern oder um einen Verweis auf ein HTML-Element festhalten zu können, das man im Folgenden manipulieren möchte. Wenn man Werte, die man nicht mehr ändert, als konstant deklariert, hat JavaScript auch bessere Möglichkeiten, die Programmausführung zu optimieren.

Empfehlung: Sparen Sie nicht an Variablen. Statt eine einzige änderbare Variable zu deklarieren und sie an mehreren Programmstellen für unterschiedliche Zwecke zu verwenden, deklarieren Sie lieber für jeden Zweck eine eigene Variable. Die Chance, dass diese dann konstant sein können, steht gut.

Es ist auch eine häufige Fehlerquelle beim Programmieren, dass man den Wert einer Variablen ändert, diese Änderung aber an einer anderen Stelle im Programm nicht berücksichtigt. Es gibt Programmiersprachen, die veränderbare Variablen gar nicht kennen, allerdings programmiert man darin auch deutlich anders als mit JavaScript.

const
erzeugt eine unveränderbare (immutable) Variable, auch symbolische Konstante genannt. Sie müssen eine solche Konstante beim Deklarieren initialisieren. Damit wird der Variablenname an einen festen Wert gebunden und behält ihn, bis die Variable ihre Gültigkeit verliert. (Hauptartikel)
let
erzeugt eine änderbare Variable, analog zu var. Sie können sie sofort initialisieren, oder den Wert auf undefined belassen und später einen Wert zuweisen. (Hauptartikel)
Variablendeklaration am Anfang eines Scripts.
const button = document.querySelector("#interaktiv"),
      output = document.querySelector("output"),
      PI     = 3.14159265359; 
let   text   = "Hallo Welt!";
let   alter;

Bei document.querySelector(...) handelt es sich um einen Zugriff auf die Programmierschnittstelle des Browsers - das DOM. Damit erhalten Sie spezielle Werte, sogenannte Objekte, mit denen Sie HTML Elemente des dargestellten Dokuments abfragen und manipulieren können.

Solche Objekte ändern sich höchstens inhaltlich, das Objekt selbst bleibt aber gültig, solange Sie es nicht ausdrücklich aus dem DOM löschen. Deswegen ist es sinnvoll, diese Objekte in einer mit const deklarierten Variablen zu speichern.

Einen „wohlbekannten“ Wert wie PI, der in Kreisberechnungen verwendet wird, kann man ebenfalls als Konstante ablegen, das ist praktischer, als ständig viele Nachkommastellen zu tippen. Es sei aber auch nicht verschwiegen, dass es ein eingebautes JavaScript Objekt Math gibt, das Ihnen π fertig bereitstellt.

Darüber hinaus deklariert das Beispiel - nur zur Demonstration - zwei veränderbare Variablen text und alter. text wird sofort mit einer Zeichenkette initialisiert.

Es gibt Diskussionen darüber, ob man Konstanten in Großbuchstaben schreiben sollte. Dies wird in vielen Programmierumgebungen bei vordefinierten Konstanten so gemacht, und es ist nicht falsch, wenn Sie das auch so halten. Bei den als const deklarierten Variablen button und output aus dem vorigen Beispiel ist das aber etwas anderes. Diese enthalten berechnete Werte, die für den jeweiligen Programmlauf gültig sind, und keine immer gleichen, wohlbekannten Werte[1].

Datentypen

Wie eingangs erwähnt, verfügt JavaScript über unterschiedliche Typen von Werten. Es gibt den speziellen Wert undefined, es gibt Zahlen, Zeichenketten, Wahrheitswerte, die schon erwähnten Objekte und die sogenannten Arrays. Objekte können Sie sich wie einen Karteikasten vorstellen, in dem Sie Werte unter einem Schlüsselbegriff ablegen können. Bei diesen Werten kann es sich wieder um beliebige JavaScript-Werte handeln. Arrays sind ebenfalls Objekte, aber ermöglichen außer Schlüsselbegriffen noch zusätzlich die Verwendung von Indexnummern.

Variablen in JavaScript müssen nicht darauf festgelegt werden, für welchen Wertetyp sie gedacht sind. Wenn Sie Java oder C++ kennen, kommt Ihnen das vermutlich ungewohnt vor, aber in JavaScript ist es so, dass jeder Wert, mit dem Sie umgehen, auch die Information enthält, welchen Datentyp er hat. Ob eine bestimmte Operation auf einen Wert anwendbar ist, wird erst geprüft, wenn das Programm ausgeführt wird.

Einige Operatoren von JavaScript verfügen auch über die Fähigkeit, sich an den Typ der Werte anzupassen, auf die man sie anwendet. So bewirkt der Operator + beispielsweise eine Addition, wenn man ihn auf Zahlen anwendet. Benutzt man ihn für Zeichenketten, werden die beiden Ketten aneinander gehängt. Der Multiplikationsoperator * hingegen versucht, Zeichenketten als Zahl zu deuten, wenn man ihn auf sie anwendet.

Diese Flexibilität bedeutet auch, dass Sie einer Variablen erst eine Zahl, dann ein Objekt, danach einen Text und zum Schluss sogar wieder undefined zuweisen können, sie macht das alles mit. Ob Sie als Programmierer danach noch wissen, was Sie mit einer solchen Variablen anfangen können, ist eine andere Frage.

Empfehlung: Verwenden Sie Variablen immer nur für genau einen Zweck, und nur für einen Datentyp.
Beispiel
// Berechnung Diagonale A4-Papier
let breite    = 210;
let höhe      = 297;
let diagonale = Math.hypot(breite, höhe);
let ausgabe   = "Die Diagonale eines A4-Blattes ist " + Math.round(diagonale) + " mm lang.";

console.log(ausgabe);

Die Werte für breite, höhe und diagonale sind Zahlen. Die Berechnung der Diagonalen verwendet die eingebaute JavaScript-Funktion Math.hypot, die genau für diesen Zweck gemacht ist (hypot steht für Hypotenuse, die Diagonale des A4-Blattes entspricht der Hypotenuse eines rechtwinkligen Dreiecks, dessen Katheten aus den Blattkanten bestehen).

Für die Ausgabe wird eine weitere eingebaute Funktion verwendet: Math.round. Sie rundet einen Wert, der Nachkommastellen besitzt, auf die nächste ganze Zahl.

Die Initialisierung von ausgabe zeigt, wie JavaScript eine Zahl, die an der Verkettung von Zeichenketten beteiligt ist, automatisch in eine Zeichenkette umwandelt und mit verkettet. In vielen anderen Programmiersprachen hätte dies eine Fehlermeldung des Sprachübersetzers oder einen Programmabbruch zur Folge. Allerdings hat sich dieses Verfahren auch als unübersichtlich erwiesen. Seit der Sprachversion „ECMAScript 2015“ verfügt JavaScript über sogenannte Template-Literale, die das Einsetzen von Werten in Zeichenketten verbessern.

Gültigkeitsbereiche

Wir haben Variablen bisher unabhängig von dem Umfeld betrachtet, in dem sie verwendet werden. Das liegt vor allem daran, dass dieser Artikel ganz am Anfang der JavaScript-Artikelreihe steht und viele Konzepte, die zum Verständnis von Gültigkeitsbereichen erforderlich sind, noch gar nicht bekannt sind. Deshalb kann es an dieser Stelle nur einen kurzen Überblick geben.

Grundsätzlich kennt JavaScript vier Arten von Gültigkeitsbereichen oder Scopes. Diese Gültigkeitsbereiche werden ausschließlich auf Basis des JavaScript-Programmtextes gebildet, den Sie verfassen. Sie hängen nicht davon ab, wie Sie Ihre erstellten Programmbausteine zur Ablaufzeit miteinander kommunizieren lassen.

Global Scope
Solange Sie keine Funktionen schreiben oder Anweisungsblöcke bilden, befinden Sie sich im Global Scope. Variablen, die darin deklariert werden, sind überall im Programm sichtbar. Den Global Scope gibt es naturgemäß nur einmal.
Module Scope
Dies ist ein Sonderfall des Global Scope für Module nach dem ECMAScript 2015 Standard. Deklarationen auf oberster Ebene, die in einem solchen Modul stattfinden, sind nur innerhalb dieses Modules sichtbar. Jedes Modul hat seinen eigenen Module Scope.
Function Scope
Funktionen sind ein Sprachmittel, um wiederverwendbaren Programmteile zu erzeugen. Dazu gehört auch, dass diese Programmteile über eigene Variablen verfügen, die bei jeder Verwendung der Funktion neu bereitgestellt werden. Variablen, die Sie in einer Funktion deklarieren, sind deshalb nur innerhalb dieser Funktion sichtbar.
Es ist möglich, innerhalb einer Funktion weitere Funktionen zu erstellen. Die Variablen der äußeren Funktion sind auch in den inneren Funktionen sichtbar und verwendbar.
Einer Funktion wird jedesmal, wenn sie aufgerufen wird, für ihren eigenen Function Scope Speicherplatz zugewiesen. Endet der Aufruf, wird seine Verbindung zu diesem Speicherbereich wieder getrennt und die Speicherverwaltung von JavaScript kann ihn löschen.
Block Scope
JavaScript-Anweisungen lassen sich zu Anweisungsblöcken zusammenfassen. Das benötigt man für die Ablaufsteuerung im Programm. Ursprünglich hat JavaScript für Anweisungsblöcke keinen eigenen Scope gebildet. Deswegen liegen var-Deklarationen niemals in einem Block Scope, sondern gehören zum Function-, Module- oder Global-Scope. In ECMAScript 2105 (ES6) kam der Block-Scope hinzu. Eine Variable, die mit const oder let deklariert wird, befindet sich im Block-Scope und verliert ihre Gültigkeit, sobald der zugehörige Anweisungsblock endet.

Da der Scope von Modulen, Funktionen und Blöcken durch den Ort bestimmt wird, an dem diese Programmteile aufgeschrieben sind, spricht man hier auch von lokalen Scopes.

Variablen, Namen und Bindung

Die Existenz mehrerer Scopes, die dazu auch noch ineinander geschachtelt sein können, wirft mehrere Fragen auf. Eine davon ist: Was geschieht, wenn eine Variable in mehr als einem Gültigkeitsbereich deklariert wurde?

Um das zu beantworten, muss man zwischen Namen und Variablen unterscheiden. Wenn man im globalen Scope eine Variable xyz deklariert, dann gibt es in dem Speicherbereich, der zum globalen Scope gehört, eine Variable, und JavaScript ordnet ihr den Namen xyz zu. Das ist die Bindung des Namens an die Variable.

Wenn Sie eine Funktion erstellen und zu Beginn eine Variable xyz deklarieren, dann machen Sie JavaScript bekannt, dass es in dieser Funktion den Namen xyz einer Variablen mit Function Scope zuordnen soll. In dem Moment, wo die Funktion aufgerufen wird, reserviert JavaScript Speicherplatz und schafft damit Platz für die Variablen, die für den Function Scope deklariert wurden.

Während die Funktion läuft, gibt es für den Namen xyz zwei Variablen. Eine davon liegt im Global Scope, ist aber während der Funktionsausführung nicht sichtbar, weil ihr Name innerhalb der Funktion an eine lokale Variable gebunden wurde. Programmcode, der außerhalb dieser Funktion aufgeschrieben ist, verwendet diese Bindung nicht.

Eine tiefergehende Diskussion dieses Thema muss dem Artikel über Funktionen vorbehalten bleiben. Auf eine Frage soll aber noch eingegangen werden: Was ist, wenn ich in einem lokalen Scope einen Namen definiert habe, der einen Namen im globalen Scope überdeckt - aber trotzdem den Bedarf habe, auf die globale Variable zuzugreifen?

Globaler Scope - Globales Objekt

Der Begriff des Objekts ist bisher noch nicht richtig definiert worden. Merken Sie sich einstweilen nur, dass ein Objekt ein Speicherbereich ist, in dem Sie Werte unter einem Namen ablegen können. Die so erstellten Einträge in diesem Speicherbereich nennen sich Eigenschaften.

Objekteigenschaften und Variablen sind sich also ziemlich ähnlich. Der Hauptunterschied ist, dass JavaScript einen Namen an Hand des Scopes automatisch an eine Variable bindet. Um auf eine Eigenschaft zuzugreifen, müssen Sie das Objekt, in dem gesucht werden soll, ausdrücklich angeben.

Variablen, die Sie im globalen Scope mit var deklarieren, werden automatisch als Eigenschaften in einem so genannten globalen Objekt bereitgestellt. Sie können das globale Objekt verwenden, um diese Variablen jederzeit zu erreichen. Der Name für dieses globale Objekt ist unterschiedlich, je nachdem, wo Sie JavaScript ausführen.

Hinweis:
Wenn Sie im globalen Objekt eine Eigenschaft erzeugen, verhält sich JavaScript so, als hätten Sie eine Variable mit diesem Namen mittels var deklariert.

JavaScript in einem Dokument eines Webbrowsers verwendet den Namen window für das globale Objekt. Außer Ihren selbst deklarierten globalen Variablen befinden sich dort buchstäblich hunderte vom Browser vordefinierte Einträge. Hinzu kommen weitere globale Variablen aus JavaScript-Bibliotheken, die Sie vielleicht nutzen möchten. Der globale Scope ist ein Kollision, die nur darauf wartet, zu passieren, deshalb sollten Sie alles tun, um ihre eigenen Variablen dort herauszuhalten. Wir werden in späteren Artikeln darauf eingehen, wie man dafür vorzugehen hat.

Sie können aus ihrem Dokument-JavaScript noch weitere JavaScript-Umgebungen starten, sogenannte Web Worker. In solchen Umgebungen ist das angezeigte Dokument nicht zugänglich, und das globale Objekt in einem Worker findet sich unter dem Namen self.

Und schließlich gibt es ein beliebtes Tool, um Serverprogramme in JavaScript schreiben zu können, node.js. Dieses Programm verwendet die JavaScript-Engine „V8“ von Google. In einem node.js-Programm gibt es ebenfalls kein HTML-Dokument, dass gerade angezeigt wird, und kein window-Objekt. Das globale Objekt in node.js nennt sich global.

Für Programmierer, die eine JavaScript-Funktion erstellen möchten, die in mehr als einer dieser Umgebungen läuft und Werte aus dem globalen Objekt benötigt, ist diese Namensvielfalt ein Problem. Sie müssen entweder drei Versionen ihrer Funktion bereitstellen, oder irgendwie erkennen, in welcher Umgebung die Funktion läuft. Um das Problem zu beheben, wurde ein einheitlicher Zugriff auf das globale Objekt spezifiziert: die Variable globalThis

Es wäre auf den ersten Blick praktischer gewesen, statt eines neuen Begriffs die Variablen window, global und self in allen Ausführungskontexten gleichermaßen bereitzustellen, aber das hätte mit Sicherheit alten Code kaputt gemacht, der nicht erwartet, diese Variablen vorzufinden.

Abzuraten ist von der früher gezeigten Idee, eine Funktion aufzurufen und darin die Kontextvariable this auszulesen. Außerhalb des strict mode funktioniert das, this enthält dann das globale Objekt. Aber moderner JavaScript-Code sollte immer im strengen Modus (strict mode) geschrieben werden, und darin funktioniert diese Methode nicht mehr. Verwenden Sie globalThis.

Empfehlungen für die Praxis

Empfehlung:
  • Verwenden Sie den strict mode, um durch Tippfehler versehentlich erzeugte globale Variablen auszuschließen.
  • Scope (Gültigkeit):
    Benutzen Sie Variablen – soweit möglich – lokal und deklarieren Sie auch dort. So wird ihr Script übersichtlicher und leichter zu debuggen.
    • Deklarieren Sie globale Variablen am Anfang des Scripts mit
      • const für unveränderliche Konstanten
      • let für Variablen
    • Halten Sie den globalen Namensraum sauber. Globale Variablen sind dank verschiedener Kapselungs-Techniken seltener erforderlich, als man glaubt, aber wenn es gar nicht anders geht, dann sollten Sie Ihre Variablen in einem Namensraum-Objekt zusammenfassen. In dessen Eigenschaften können Sie globale Informationen für Ihr Projekt ablegen.
    • lokale Variablen können Sie mit const oder let deklarieren.
  • Variablennamen
    Benutzen Sie sprechende Namen, d. h. sinnvolle Bezeichnungen, die sich später zurückverfolgen lassen.
    ausgabe = breite * höhe; ist besser als a = b * h;
    • Achten Sie bei den Namen für Ihre eigenen Variablen und Funktionen aber darauf, dass es sich dabei nicht um bekannte Namen aus den Programmierschnittstellen des Browsers handelt. Damit senken Sie die Gefahr von Namenskollisionen.
  • Beachten Sie die Regeln für selbstvergebene Namen, z.B:
    • Variablen sind case-sensitive, also achten Sie immer auf Groß-und Kleinschreibung.
    • CamelCase
      Besser lesbar ist das so genannte CamelCase, in dem die ersten Buchstaben der einzelnen Bestandteile des Namens groß geschrieben werden.


Weblinks

  1. SELF:Forum: const in CAPITALS? vom 06.01.2021