Wenn eine Variable innerhalb einer Funktion oder einer Schleife deklariert wird, ist sie auch außerhalb dieser Funktion oder des Code-Blocks sichtbar? Darüber entscheidet die Sichtbarkeit von Variablen, also der variable scope. Dabei wird grundsätzlich zwischen zwei scopes unterschieden: functional scope und block scope.
Zwei weitere Konzepte, die für die Definition der Sichtbarkeit einer Variable relevant sind, sind lexical scope und hoisting.
Variablen, die per var definiert werden, haben einen functional scope. Das heißt, sie sind innerhalb der gesamten Funktion sichtbar, in der sie deklariert wurden - und in allen Funktionen, die innerhalb der Funktion definiert werden. Aber nicht außerhalb.
Beispiel:
function testingScope1() {
var condition = true;
var myVarMessage = "Hello var!";
if (condition) {
console.log(myVarMessage); // "Hello var!"
}
}
console.log(myVarMessage); // Error: undefined;
Variablen können in einem child scope auch neu deklariert werden, was auch die ursprüngliche Variable neu setzt. Dies kann zu unerwartetem Verhalten führen.
Beispiel:
var myVar = 'outside';
if(true){
var myVar = 'inside'
console.log(myVar); //output: "inside"
}
console.log(myVar); //output: "inside"
Deswegen wird seit ES6 empfohlen, statt "var" besser "const" oder "let" zu verwenden, die beide einen block scope haben.
Seit ECMA Script 6 (ES 6) unterstützt JavaScript auch nativ block scope-Variablen, also Variablen, die nur innerhalb eines Code-Blocks sichtbar sind. Dafür wurden die beiden neuen Keywords let und const eingeführt.
Ein "block" ist der Bereich innerhalb einer if-Bedingung, einer for- oder while-Schleife und einer switch-Anweisung. Allgemein kann man sagen: Alles zwischen zwei {}- Klammern ist ein Block.
Variablen/Konstanten, die mit let oder const deklariert werden, sind also nur innerhalb des Blocks sichtbar, wo sie deklariert werden.
Ein Beispiel:
function testingScope3(){
if(true){
var myVar = 'I am a var';
const myConst = 'I am a const';
let myLet = 'I am a let';
}
console.log(myVar);
console.log(myConst);
console.log(myLet);
}
testingScope3();
// output:
// "I am a var"
// error: myConst is not defined
// error: myLet is not defined
Eine neue Zuweisung einer Variable ist auch mit let möglich, dies überschreibt dann den Wert der ursprünglichen Variable - allerdings nur für den Block der neuen Variable.
Beispiel:
let myLet = 'outside';
if(true){
let myLet = 'inside'
console.log(myLet); //Output: "inside"
}
console.log(myLet); //Output: "outside"
Der Scope von Kindern hat immer Zugriff auf die Variablen im Parent Scope. Das nennt man "lexical scope".
Beispiel:
function testingScope() {
var myConstMessage = "Hello const!";
function innerFunction() {
console.log(myConstMessage );
}
innerFunction(); // output: "Hello const!"
}
JavaScript-Deklarationen von var und Funktionen werden "hochgezogen" (hoisted) zum Anfang ihres jeweiligen Scopes. Das heißt, sie können verwendet werden vor ihrer eigentlichen Deklarationen.
Ein Beispiel:
x = 5;
console.log(x); // output: 5
var x;
Achtung: Nur Deklarationen werden hoisted, nicht die Initialisierung.
Ein Beispiel:
console.log(y); // output: undefined
var y = 7;
console.log(y); // output: 7
Bei dem ersten console.log ist zwar die Variable dank des hoisting bereits deklariert (sonst gäbe es einen ReferenceError: y is not defined), aber nicht die Initialisierung.
Zwei Hinweise:
Da viele Entwickler das hoisting-Verhalten nicht kennen, kann es zu unerwarteten Fehlern führen. Deswegen wird empfohlen, Deklarationen stets selbst am Anfang des Scopes zu definieren - so wie es JavaScript vom Verhalten her am Ende sowieso tut.
Im "strict mode" (use strict) erlaubt JavaScript nicht die Verwendung von Variablen, bevor sie deklariert wurden.