tutorials / javascript-variablen-und-scopes

JavaScript Variablen und Scopes

Variablen sind Container für Daten in deinem Programm. In JavaScript gibt es drei Wege, Variablen zu deklarieren: var, let und const. Jede hat unterschiedliche Eigenschaften und Einsatzbereiche. In diesem Tutorial lernst du, welche du wann nutzen solltest und was Scopes sind.

Variablen deklarieren: var, let, const

let – Die moderne Variablendeklaration

let ist seit ES6 (2015) der Standard für veränderbare Variablen:

let name = 'Max';
console.log(name); // Max

name = 'Maxine'; // Wert ändern ist erlaubt
console.log(name); // Maxine

let age; // Deklaration ohne Initialisierung
console.log(age); // undefined
age = 30; // Später zuweisen

Wann nutzen? Wenn der Wert sich ändern soll (Counter, User-Input, Loop-Variablen).

const – Konstanten für unveränderliche Werte

const erstellt Konstanten, die nach der Initialisierung nicht neu zugewiesen werden können:

const PI = 3.14159;
console.log(PI); // 3.14159

PI = 3.14; // ❌ TypeError: Assignment to constant variable

// const muss sofort initialisiert werden
const birthYear; // ❌ SyntaxError: Missing initializer

Wichtig: Bei Objekten/Arrays schützt const nur die Referenz, nicht den Inhalt:

const user = { name: 'Max', age: 25 };
user.age = 26; // ✅ Erlaubt – Objekt-Eigenschaften ändern
console.log(user); // { name: 'Max', age: 26 }

user = { name: 'Anna' }; // ❌ TypeError – Neu-Zuweisung verboten

const numbers = [1, 2, 3];
numbers.push(4); // ✅ Erlaubt – Array mutieren
console.log(numbers); // [1, 2, 3, 4]

numbers = [5, 6]; // ❌ TypeError – Neu-Zuweisung verboten

Wann nutzen? Standard für alle Variablen, außer du weißt, dass du neu zuweisen musst. Modern JavaScript bevorzugt const.

var – Die alte Variablendeklaration (vermeiden!)

var war bis ES6 die einzige Option, hat aber problematische Eigenschaften:

var oldStyle = 'Legacy Code';
console.log(oldStyle); // Legacy Code

oldStyle = 'Neuer Wert'; // Neu-Zuweisung erlaubt (wie let)

Warum var vermeiden?

  1. Function Scope statt Block Scope (siehe unten)
  2. Hoisting mit undefined (verwirrend)
  3. Re-Deklaration erlaubt (fehleranfällig)
var x = 5;
var x = 10; // ✅ Erlaubt, aber verwirrend!
console.log(x); // 10

let y = 5;
let y = 10; // ❌ SyntaxError: Identifier 'y' has already been declared

Faustregel: Nutze var nur in Legacy-Code, den du nicht ändern darfst.

Variable Scopes: Wo ist eine Variable sichtbar?

Scope bestimmt, wo im Code eine Variable verfügbar ist. JavaScript hat drei Scope-Arten:

1. Block Scope (let, const)

Ein Block ist Code zwischen { }. let und const sind nur innerhalb ihres Blocks sichtbar:

{
  let blockVar = 'Nur hier sichtbar';
  console.log(blockVar); // ✅ Nur hier sichtbar
}

console.log(blockVar); // ❌ ReferenceError: blockVar is not defined

Praktisches Beispiel – if-Block:

let score = 85;

if (score >= 50) {
  let message = 'Bestanden!';
  console.log(message); // ✅ Bestanden!
}

console.log(message); // ❌ ReferenceError – message existiert nur im if-Block

Loops isolieren Variablen:

for (let i = 0; i < 3; i++) {
  console.log(i); // 0, 1, 2
}

console.log(i); // ❌ ReferenceError – i existiert nur im Loop

Mit var passiert das NICHT:

for (var j = 0; j < 3; j++) {
  console.log(j); // 0, 1, 2
}

console.log(j); // ✅ 3 – var "leakt" aus dem Block!

2. Function Scope (var, let, const)

Variablen innerhalb einer Funktion sind immer nur dort sichtbar (egal ob var, let, const):

function myFunction() {
  var funcVar = 'Nur in der Funktion';
  let funcLet = 'Auch nur hier';
  const funcConst = 'Ebenfalls lokal';

  console.log(funcVar, funcLet, funcConst); // ✅ Alle verfügbar
}

myFunction();
console.log(funcVar); // ❌ ReferenceError – alle nicht verfügbar außerhalb

Nested Functions (verschachtelt):

function outer() {
  let outerVar = 'Außen';

  function inner() {
    let innerVar = 'Innen';
    console.log(outerVar); // ✅ Zugriff auf outer-Variable
    console.log(innerVar); // ✅ Eigene Variable
  }

  inner();
  console.log(innerVar); // ❌ ReferenceError – innerVar ist im inner-Scope
}

outer();

Regel: Innere Funktionen können auf äußere Variablen zugreifen, aber nicht umgekehrt.

3. Global Scope

Variablen außerhalb von Funktionen/Blöcken sind global:

let globalVar = 'Überall verfügbar';

function test() {
  console.log(globalVar); // ✅ Zugriff möglich
}

test();
console.log(globalVar); // ✅ Auch hier

Im Browser: Globale var-Variablen werden zu window-Eigenschaften:

var globalWithVar = 'test';
console.log(window.globalWithVar); // ✅ 'test'

let globalWithLet = 'test';
console.log(window.globalWithLet); // ❌ undefined – let/const nicht am window

Best Practice: Vermeide globale Variablen (Namespace-Kollisionen, schwer zu debuggen).

Hoisting: Variablen “nach oben ziehen”

JavaScript verschiebt Deklarationen an den Anfang ihres Scopes (Hoisting), aber nicht die Initialisierung.

Hoisting mit var (problematisch)

console.log(x); // undefined (nicht ReferenceError!)
var x = 5;
console.log(x); // 5

Was passiert intern:

var x; // Deklaration wird "hochgezogen"
console.log(x); // undefined – Variable existiert, hat aber keinen Wert
x = 5; // Initialisierung bleibt hier
console.log(x); // 5

Hoisting mit let/const (Temporal Dead Zone)

let und const werden auch gehoisted, aber landen in der Temporal Dead Zone (TDZ) – Zugriff vor Deklaration ist verboten:

console.log(y); // ❌ ReferenceError: Cannot access 'y' before initialization
let y = 10;

Temporal Dead Zone visualisiert:

// TDZ starts here for 'myVar'
console.log(myVar); // ❌ ReferenceError
let myVar = 5; // TDZ ends – ab hier verfügbar
console.log(myVar); // ✅ 5

Warum ist das gut? Erzwingt sauberen Code – Variablen müssen deklariert werden, bevor du sie nutzt.

Funktions-Hoisting

Funktionsdeklarationen werden komplett gehoisted:

greet(); // ✅ Funktioniert! Output: Hallo!

function greet() {
  console.log('Hallo!');
}

Aber: Function Expressions (mit const/let) werden nicht gehoisted:

sayHi(); // ❌ ReferenceError: Cannot access 'sayHi' before initialization

const sayHi = function() {
  console.log('Hi!');
};

Variable Naming Rules

JavaScript hat klare Regeln für Variablennamen:

Erlaubt:

let userName = 'Max';       // ✅ camelCase (Best Practice)
let _private = 'secret';    // ✅ Underscore erlaubt
let $jquery = 'library';    // ✅ Dollar-Zeichen erlaubt
let user2 = 'Second User';  // ✅ Zahlen erlaubt (nicht am Anfang!)
let währung = 'Euro';       // ✅ Unicode (aber nicht empfohlen)

Verboten:

let 2user = 'Invalid';      // ❌ Zahl am Anfang
let user-name = 'Invalid';  // ❌ Bindestrich nicht erlaubt
let class = 'Reserved';     // ❌ Reserved Keyword
let let = 'Invalid';        // ❌ Reserved Keyword

Reserved Keywords (nicht verwenden):

break, case, catch, class, const, continue, debugger, default, delete,
do, else, export, extends, finally, for, function, if, import, in,
instanceof, let, new, return, super, switch, this, throw, try, typeof,
var, void, while, with, yield

Naming Conventions (Best Practices):

// camelCase für Variablen und Funktionen
let userName = 'Max';
function getUserData() { }

// PascalCase für Klassen
class UserAccount { }

// UPPER_CASE für Konstanten (echte Konstanten, nicht nur const)
const API_KEY = 'abc123';
const MAX_RETRIES = 3;

// Beschreibende Namen
let age = 25;              // ✅ Gut
let a = 25;                // ❌ Nicht aussagekräftig

// Boolean mit is/has/can Präfix
let isActive = true;
let hasPermission = false;
let canEdit = true;

Scope Chain: Wie JavaScript Variablen findet

JavaScript sucht Variablen von innen nach außen:

let global = 'global';

function outer() {
  let outerVar = 'outer';

  function inner() {
    let innerVar = 'inner';

    console.log(innerVar);  // 1. Sucht in innerem Scope ✅
    console.log(outerVar);  // 2. Sucht in outer Scope ✅
    console.log(global);    // 3. Sucht in global Scope ✅
    console.log(notExist);  // 4. Nicht gefunden ❌ ReferenceError
  }

  inner();
}

outer();

Shadowing (Überschatten):

let name = 'Global Max';

function test() {
  let name = 'Local Anna'; // Überschattet die globale Variable
  console.log(name);       // Local Anna
}

test();
console.log(name); // Global Max – unverändert

Praktische Beispiele

Counter mit let vs. var in Loops

Problem mit var:

for (var i = 0; i < 3; i++) {
  setTimeout(() => {
    console.log(i); // Output: 3, 3, 3 (!)
  }, 100);
}

Warum? var hat Function Scope – alle Timeouts teilen sich dasselbe i, das am Ende 3 ist.

Lösung mit let:

for (let i = 0; i < 3; i++) {
  setTimeout(() => {
    console.log(i); // Output: 0, 1, 2 ✅
  }, 100);
}

Warum? let hat Block Scope – jede Iteration bekommt ihr eigenes i.

Konfiguration mit const

const CONFIG = {
  apiUrl: 'https://api.example.com',
  timeout: 5000,
  debug: true
};

// Eigenschaften ändern ist OK
CONFIG.debug = false; // ✅

// Neu-Zuweisung verboten
CONFIG = { }; // ❌ TypeError

Best Practices Zusammenfassung

  1. Standard: const – Verwende const für alles, was nicht neu zugewiesen wird
  2. Bei Bedarf: let – Nutze let für Counter, Flags, Re-Assignments
  3. Niemals: var – Vermeide var in neuem Code
  4. Block Scope nutzen – Variablen so lokal wie möglich definieren
  5. Beschreibende NamenuserAge statt a
  6. UPPER_CASE für Konstantenconst MAX_SIZE = 100
// ✅ Modern und sauber
const API_ENDPOINT = 'https://api.example.com';
let requestCount = 0;

function fetchData() {
  const url = `${API_ENDPOINT}/users`;
  requestCount++; // let erlaubt Änderung

  // url bleibt lokal im Function Scope
  console.log(url);
}

Nächste Schritte

Du verstehst jetzt:

  • Den Unterschied zwischen var, let und const
  • Block, Function und Global Scope
  • Hoisting und Temporal Dead Zone
  • Naming Rules und Best Practices

Im nächsten Tutorial lernst du Datentypen und Type Casting – welche Arten von Daten JavaScript kennt und wie sie konvertiert werden.

Weiterführende Ressourcen

Tipp: Experimentiere in der Browser-Console mit verschiedenen Scope-Szenarien, um das Verhalten zu verinnerlichen!