Das ist nichts anderes als ein weiterer Designfehler von Javascript. Wenn ich lesend auf etwas zugreife, was es aber nicht gibt, sollte wenigstens eine Warnung kommen.
Ach komm :) Ich würde sagen, Javascript funktioniert einfach nur nicht so, wie du es gerne hättest.
Du greifst in diesem Fall nicht auf einen Speicherbereich zu der nicht existent ist, sondern auf Prototypen, welche zwischen Objektbäumen vererbt werden.
Was ich damit sagen will: andere Sprache, andere Konzepte. Du kannst nicht Regeln, oder Best Practices von C, Rust, Go ... auf Javascript anwenden. Oder gehörst du etwa auch zu denjenigen, die sich über die furchtbare Implementierung von Namespaces in CSS echauffieren? :)
Du bekommst deinen Referenz-Error, wenn du eine Variable in einem Scope definierst und nicht eine Objekteigenschaft.
Hier ein "schönes" Beispiel:
// alert(foo); // undefined
// alert(baz); // reference error: baz is not defined
// alert(window.bar) // undefined
// alert(bar); // reference error: bar is not defined
bar = 100;
var foo = 200;
let baz = 300;
// alert(foo); // 100
// alert(bar); // 200
// alert(window.bar) // 200
Und hier eine genaue Erklärung. Keine Rechtfertigung.
Meine Erklärung sieht so aus: Wenn du auf eine Eigenschaft eines Objekt zugreifst durchläuftst du die ganze Prototypen-Kette durch. Wenn keine Eigenschaft gefunden wird erhälst du 'undefined'. Nicht definiert, da die Eigenschaft nicht definiert ist. Du greifst nicht auf einen undefinierten Speicherbereich zu, deshalb auch kein Referenz-Error. Mehr dazu.
Wenn du hingegen auf eine Variable in einem Scope zugreifst, greifst du effektiv auf einen Speicherbereich zu. Deshalb gibt es auch einen Referenz-Error.
Einen Vorteil, den ich darin sehe ist, dass ich lange Eigenschaftsketten bequem validieren kann, ohne jedes einzelne Glied auf Existenz checken zu müssen, bevor ich beispielsweise den Typ abfrage.
Ein weiterer Vorteil ist, dass ich Scripts in mehrere Dateien aufteilen kann mit folgendem sog. Module-Pattern, ohne mir über die Lade-Reihenfolge Gedanken machen zu müssen:
var module = (function (def) {
// code
return def;
}(module || {}));
Das würde einen Referenz-Error werfen, da ich module
an die Funktion übergebe, bevor ich module
definiere. Das ist übrigens nicht irgend ein Pattern, sondern ein grundlegende Vorgehensweise zur Strukturierung von Code in ECMAScript 2015.
lg
mark