Orlok: const let var

Beitrag lesen

Hallo @Auge

Zwei Fragen stellen sich mir dennoch.

Sorry, dass die Antworten auf diese Fragen so lange auf sich haben warten lassen. ;-)

Aber zunächst muss ich noch eine kleine Korrektur meiner ersten Antwort vornehmen, denn da habe ich in einem Beispiel zur Verwendung der Methode Array.prototype.indexOf leider einen Fehler eingebaut:

const array = ['garfield', 'odie'];

// if (!~array.indexOf('garfield')) {

if (~array.indexOf('garfield')) {
  console.log(true); // true
}

Der logische vor dem binären NOT Operator ist hier natürlich Unsinn, da die Ausgabe ja nicht erfolgen soll wenn der Wert nicht in dem Array enthalten ist, sondern wenn er enthalten ist. :-P

Warum aside als Konstante und nicht als Variable?

Also du meinst, warum ich für aside nicht var verwendet habe? Oder weshalb ich nicht statt const eine Variable mit let angelegt habe? Oder einfach ganz allgemein?

Naja, wenn man den konkreten Fall isoliert betrachtet, dann wäre es egal gewesen, auf welche Weise man den Wert hinterlegt. Man hätte sich hier eine entsprechende Deklaration auch komplett sparen können, zumal die Referenz auf das Elementobjekt ja nur an einer Stelle gebraucht wird.


Jedenfalls, kurz gesagt, habe ich hier const verwendet, weil ich const immer dann verwende, wenn kein anderer Wert an den ausgewählten Bezeichner gebunden werden soll, also die Bindung von Bezeichner und Wert konstant bleibt. ;-)


Allerdings wäre es vielleicht ganz hilfreich, einmal die wesentlichsten Unterschiede zwischen den verschiedenen Variablentypen zu beschreiben, um meine Entscheidung besser nachvollziehen zu können.

Scope

Dabei wäre zunächst mal auf den Gültigkeitsbereich (Scope) von Variablen einzugehen. Denn hier besteht ein wesentlicher Unterschied zwischen solchen Variablen die mit var deklariert werden und solchen, die mittels let oder const in die Welt gesetzt werden.

// global environment
var number = 16;

function test (condition) {
  // function environment
  if (condition) {
    var number = 32;
  }
  console.log(number); // 32
}

test(true);

console.log(number); // 16

Variablen die mittels var deklariert werden sind in Bezug auf ihre Sichtbarkeit mit dem jeweiligen Ausführungskontext (Execution Context) verknüpft. Sie sind also grundsätzlich an die lexikalische Umgebung eines Scripts, eines Moduls oder einer Funktion gebunden.

Mit Blick auf das Beispiel oben heißt das, dass die innerhalb der globalen Umgebung deklarierte Variable mit dem Bezeichner number im gesamten Programm sichtbar ist, und dass die innerhalb der Funktion test deklarierte Variable überall innerhalb dieser Funktion Gültigkeit besitzt.

// global environment
let number = 16;

function test (condition) {
  // function environment
  if (condition) {
    // block environment
    let number = 32;
  }
  console.log(number); // 16
}

test(true);

console.log(number); // 16

Bei let und const sieht das Ganze hingegen ein wenig anders aus, wie das beinahe identische Beispiel oben zeigt, denn anders als bei var-Variablen wird hier auch durch einen Block ein Gültigkeitsbereich definiert (Block Scope), weshalb die innerhalb des Anweisungsblocks der if-Anweisung notierte Deklaration der Variable number keine Auswirkungen auf den Rest des Funktionskörpers von test hat, sodass hier schließlich der Wert der globalen Variablen number in die Konsole geschrieben wird.

(function test (condition) {
  // function environment
  const number = 8;
  if (condition) {
    // block environment
    const number = 64;
    console.info(number); // 64
  }
  console.info(number); // 8
}(true));

Das heißt, die Definition eines neuen Gültigkeitsbereichs bei var ist nur durch Einschluss der Deklaration in eine Funktion möglich, weshalb vor der Standardisierung von let und const oft unmittelbar aufgerufene Funktionsausdrücke (IIFE) zu diesem Zweck verwendet wurden, also eine Syntax wie bei der Funktion test in dem Beispiel oben. Mit let und const ist es hingegen möglich, im selben Ausführungskontext eine Kette von Gültigkeitsbereichen zu implementieren.

Darüber hinaus ist Block Scope aber nicht der einzige Unterschied hinsichtlich der Sichtbarkeit der Variablen, wie das folgende Beispiel zeigt:

function test (condition) {
  for (let index = 0; index < 3; index ++) {
    const value = 'message';
    if (condition) {
      console.info(value); // 3x message
    }
  }
  console.info(index); // Reference Error - index is not defined
}

test(true);

Die im Anweisungsblock der Schleife deklarierte Konstante besitzt wie gesehen nur Gültigkeit innerhalb dieses Blocks, das heißt, sie wird bei jeder Iteration neu erzeugt und ist außerhalb des Blocks nicht sichtbar. So weit, so klar. Aber auch die mittels let im Schleifenkopf deklarierte Variable ist in ihrer Sichtbarkeit auf die Schleife beschränkt, weshalb der Versuch ihren Wert auszugeben hier einen Reference Error erzeugt.

const captains = new Map([
  ['Kirk', 'James Tiberius'],
  ['Picard', 'Jean-Luc']
]);

for (let entry of captains) {
  console.info(entry.reverse( ).join(' ')); // James Tiberius Kirk, Jean-Luc Picard
}

console.info(entry); // Reference Error - entry is not defined

Wie hier zu sehen ist gilt das nicht nur für gewöhnliche for-Schleifen, sondern ebenso für andere Schleifentypen. Das bedeutet, mittels let oder const deklarierte Variablen sind außerhalb der Schleife grundsätzlich nicht sichtbar.

Bei Variablen die mit dem Schlüsselwort var deklariert wurden verhält es sich bekanntermaßen anders. Bezogen auf das folgende Beispiel können die Variablen index und value auch außerhalb der Schleife referenziert werden.

for (var index = 1700; index < 1701; index += 1) {
  var value = 'Enterprise';
}

console.info(index); // 1701

console.info(value); // Enterprise

Es ist also festzuhalten, dass const und let im Vergleich zu var eine leichtere Kontrolle hinsichtlich ihrer Sichtbarkeit ermöglichen. Wenn man davon ausgeht dass Variablen prinzipiell so lokal wie möglich angelegt werden sollten, dann besitzen let und const hier also einen Vorteil gegenüber Variablen die mit var deklariert werden.

Hoisting / Temporal Dead Zones

Wie ich in meiner ersten Antwort bereits erwähnte, werden var-Variablen ebenso wie Funktionen die deklariert wurden gehoistet, also bei der Initialisierung des jeweiligen Ausführungskontextes an den Anfang des ausführbaren Codes gezogen, wobei der Variable zunächst der Wert undefined zugewiesen wird.

// global environment
var word = 'foo';

function test (condition) {
  // function environment
  console.info(word); // undefined
  if (condition) {
    var word = 'bar';
  }
}

test(false);

Da var-Variablen immer Funktionsweit sichtbar sind bedeutet das, dass selbst wenn wie in dem Beispiel oben der Anweisungsblock des Conditional Statements nie betreten wird, die innerhalb der Funktion deklarierte Variable die gleichnamige Variable der äußeren lexikalischen Umgebung verschattet.

Das heißt, wann immer irgendwo innerhalb ihres Gültigkeitsbereichs eine Variable mit var deklariert wird, erfolgt die Erzeugung (und die Initialisierung mit undefined) der Variable bereits vor der Ausführung irgendwelcher Anweisungen in dem jeweiligen Codeabschnitt, lediglich die Zuweisung des Wertes (Assignment) erfolgt erst an der Stelle im Code an der sie notiert wurde.

Entsprechend wird beim Zugriff auf eine var-Variable vor ihrer Deklaration im Code auch keine Ausnahme geworfen, weder im Sloppy Mode noch im Strict Mode. – Anders als bei dem Versuch, eine mittels const oder let angelegte Variable vor ihrer Deklaration im Code zu referenzieren, denn hier wird unabhängig vom Ausführungsmodus des Programms grundsätzlich eine Fehler produziert.

function test ( ) {
  try {
    console.info(foo);
  } catch (exception) {
    console.error(exception); // Reference Error
  }
  let foo = 5;
}

test( );

console.info(bar); // Reference Error

const bar = 3;

Zwar findet auch hier die Bindung von Bezeichner und lexikalischer Umgebung des jeweiligen Abschnitts statt, bevor in diesem Abschnitt notierte Anweisungen ausgeführt werden, aber die Variablen werden dabei nicht initialisiert, sondern befinden sich zunächst in einem Zustand der inoffiziell als Temporal Dead Zone bezeichnet wird. Sie sind also für die Zeit bis zu ihrer tatsächlichen Deklaration im Code nicht referenzierbar.

function test ( ) {
  let foo = 1;
  if (true) {
    // temporal dead zone start
    console.log(foo); // Reference Error
    // temporal dead zone end
    let foo = 3;
  }
}

test( );

Dass die Bindung bereits beim Eintritt in den jeweiligen Gültigkeitsbereich erfolgt zeigt das Beispiel oben. Sprich, es wird nicht etwa die gleichnamige Variable des äußeren Kontextes referenziert, sondern es wird, da die lexikalische Umgebung des Anweisungsblocks über eine Bindung für den Bezeichner foo verfügt, eine Ausnahme geworfen.

function outer ( ) {
  // nested function
  function inner ( ) {
    console.info(foo); // bar
  }
  // declaration
  const foo = 'bar';
  // call
  inner( );
}

outer( );

Wie hier zu sehen ist, handelt es sich dabei tatsächlich um eine Temporal Dead Zone, denn entscheidend ist nicht der Ort der Notierung (man beachte, dass inner hier deklariert und mithin ohnehin gehoistet wird), sondern der zeitliche Ablauf des Programmflusses, weshalb die Notierung von inner vor der Deklaration der Konstante unproblematisch ist.

Da die Verwendung von Variablen vor ihrer Deklaration ohnehin als schlechte Praxis anzusehen ist, weil hierbei immer das Risiko besteht, dass die spätere Deklaration vergessen wird, und der Code darüber hinaus auch schlechter lesbar ist, kann man wohl sagen, dass const und let aufgrund ihrer strictness in dieser Hinsicht eher geeignet sind einen guten Programmierstil zu fördern, als var es ist.

Aber wie dem auch sei, ist an dieser Stelle noch auf eine weitere Besonderheit in Zusammenhang mit der Temporal Dead Zone von const- und let-deklarierten Variablen hinzuweisen, welche bei der Verwendung des Operators typeof zu beobachten ist.


Aber dazu kommen wir erst im nächsten Teil der Antwort, da dieser Beitrag kurz davor ist die Zeichenbegrenzung zu sprengen und ich nicht mitten im Satz abbrechen will. ;-)

Also: Fortsetzung folgt!

0 49

Regel in CSS Klasse ändern

Blumentopf
  • css
  • javascript
  1. 1
    Auge
    1. 0
      Gunnar Bittersmann
  2. 0
    Matthias Apsel
    • css
    • jsp
    1. 0
      Auge
      • css
      • javascript
      1. 0
        Matthias Apsel
        1. 0
          Auge
          • sprache
    2. 0
      Matthias Apsel
      1. 0
        Gunnar Bittersmann
        • css
        1. 0
          Matthias Apsel
          1. 0
            Gunnar Bittersmann
  3. 0
    Blumentopf
    1. 0
      Auge
      • css
      • html
      • javascript
      1. 0
        Blumentopf
        1. 0
          Auge
          1. 0
            Blumentopf
            1. 0
              Matthias Apsel
            2. 0
              Auge
              1. 0
                Blumentopf
              2. 0
                Orlok
                1. 0
                  Auge
                  1. 0

                    "Funktion" im Alltag

                    Der Martin
                    • sonstiges
                    1. 0
                      Auge
                      • menschelei
                      • sonstiges
                      1. 0
                        Der Martin
                        1. 0
                          Auge
                          1. 0
                            Der Martin
                    2. 0
                      Tabellenkalk
                      1. 0
                        Der Martin
                    3. 0
                      Der Martin
                      1. 1
                        JürgenB
                        • menschelei
                        • sonstiges
                        1. 0
                          Der Martin
                          1. 0
                            JürgenB
                            1. 0
                              Der Martin
                  2. 4

                    Arraymethoden und anonyme Funktionen

                    Orlok
                    • javascript
                  3. 2

                    const let var

                    Orlok
                    • javascript
                    1. 3
                      Orlok
                    2. 0
                      Auge
                      1. 1

                        Konstanten

                        Orlok
                        • javascript
                        • php
                        1. 0
                          Auge
    2. 0
      Gunnar Bittersmann
      • css
      • internationalisierung
      • javascript
      1. 0
        Gunnar Bittersmann
      2. 0
        Blumentopf
        1. 0
          Der Martin
          1. 0
            Blumentopf
            1. 0
              Der Martin
        2. 0
          Matthias Apsel
          1. 0
            Blumentopf
            1. 0
              Matthias Apsel
        3. 0
          Gunnar Bittersmann