JS Scoping, Hoisting und var/let/const

JavaScript (ähnlich wie in PHP) ist eine dynamisch typisierte Skriptsprache, was bedeutet, dass eine Variable keinen fixen Typ hat.

myvar = 1;
myvar = “Ich bin ein Text”;
myvar = ["Chevrolet", "Ford", "Audi"];

In statisch typisierten Programmiersprachen wie z.B. Java ist es nicht erlaubt z.B. einen Integer Wert in eine String Variable zu schreiben.

Scope

Lokale Variablen

// code here can NOT use carName

function myFunction() {
  var carName = "Ford";
  // code here CAN use carName
}

Globale Variablen

var carName = "Volvo";

// code here can use carName

function myFunction() {
  // code here can also use carName
}

Hoisting

Hoisting ist das „automatische verschieben von Variablen-Definitionen an den Anfang des aktuellen Scopes“.

D.h. die folgenden 2 Skripte verursachen 1:1 das gleiche Ergebnis:

x = 5; // Assign 5 to x

elem = document.getElementById("demo"); // Find an element
elem.innerHTML = x;                     // Display x in the element

var x; // Declare x
var x; // Declare x
x = 5; // Assign 5 to x

elem = document.getElementById("demo"); // Find an element
elem.innerHTML = x;                     // Display x in the element

Dies betrifft aber NUR Variablen, die mit „var“ definiert wurden.
„let“ und „const“ haben kein Hoisting-Verhalten.

Hier ist aber eine wichtige Unterscheidung zu machen! Hoisting betrifft NUR die Variablen-Defintion, nicht die Initialisierung mit einem Wert.

D.h. die folgenden 2 Skripte verursachen ein unterschiedliches Ergebnis:

var x = 5; // Initialize x
var y = 7; // Initialize y

elem = document.getElementById("demo"); // Find an element
elem.innerHTML = x + " " + y;           // Display x and y
var x = 5; // Initialize x

elem = document.getElementById("demo"); // Find an element
elem.innerHTML = x + " " + y;           // Display x and y

var y = 7; // Initialize y

Im 2 Beispiel erhalten wir zwar keinen Fehler da die Definition von „var y“ nach oben gehoisted wird jedoch die Wert-Zuweisung auf 7 geschieht erst am Ende.

D.h. das 2 Beispiel ist im Prinzip folgender Code:

var x = 5; // Initialize x
var y;     // Declare y

elem = document.getElementById("demo"); // Find an element
elem.innerHTML = x + " " + y;           // Display x and y

y = 7;    // Assign 7 to y

var vs let & const

Mit ES6 (ECMAScript 2015) wurden 2 neue Möglichkeiten eingefügt wie man Variablen definiert:

let ist eine veränderbare Variable, const ist eine nicht veränderbare Variable.

D.h. eine mit let definierte Variable kann jederzeit wieder neue Werte erhalten, jedoch eine mit const definierte Variable kann nur 1 mal einen Wert zugewiesen bekommen. Falls man nach der ersten Zuweisung noch einmal den Wert einer const definierten Variable ändern will erhält man einen Fehler.

Jedoch der Hauptunterschied von var zu let & const ist der, dass sowohl let als auch const definierte Variablen nur in dem jeweilig Scope verfügbar sind und nicht nach außen hin weiter gesetzt werden.

Hier ein paar Beispiele mit dem „alten“ var System und dem neuen let/const System.

var x = 10;
// Here x is 10
{
  var x = 2;
  // Here x is 2
}
// Here x is 2
var x = 10;
// Here x is 10
{
  let x = 2;
  // Here x is 2
}
// Here x is 10

Falls aber eine Variable „global“ mit let definiert wird ist diese auch in allen Funktion-Scopes erhältlich, also sozusagen eine „globale“ Variable:

let x = 10;

function myFunc(){
  console.log(x);
}

myFunc();

// Outputs 10

Jedoch darf in der Funktion myFunc nicht noch einmal ein let x ausgeführt werden:

let x = 10;

function myFunc(){
  console.log(x);
  let x = 2;
  console.log(x);
}

myFunc();
console.log(x);

VM611:4 Uncaught ReferenceError: Cannot access 'x' before initialization
    at myFunc (<anonymous>:4:15)
    at <anonymous>:9:1

Es darf nur „normal“ auf die x Variable zugegriffen werden:

let x = 10;

function myFunc(){
  console.log(x);
  x = 2;
  console.log(x);
}

myFunc();
console.log(x);

// Outputs
10
2
2

D.h. der Scope einer Variable wird in dem Moment definiert, wo let oder const ausgeführt wird, nicht in welchem Scope auf die Variable zugegriffen wird bzw. neue Werte in die Variable geschrieben werden.

Sources:
https://www.w3schools.com/js/js_scope.asp
https://www.w3schools.com/js/js_hoisting.asp

AMD, CJS, UMD, ESM – Modulares JavaScript

Zur Erinnerung: JavaScript kann sowohl im Frontend (Browser) als auch im Backend (Serverseitig über Node.js) ausgeführt werden. Die hier beschriebenen Methoden sind teilweise für Frontend und/oder Backend anwendbar.

Asynchronous Module Definition (AMD)

AMD ist eine Art und Weise wie man JavaScript Module implementieren kann, die asynchron geladen werden.

define(['dep1', 'dep2'], function (dep1, dep2) {
    //Define the module value by returning a value.
    return function () {};
});

AMD wurde primär nur für das Frontend (also Browser) entwickelt, nicht fürs Backend. Hauptsächlich wird hier RequireJS als Implementation dieser Methode verwendet.

D.h. damit diese Module im Browser funktionieren muss als erstes RequireJS geladen werden und danach erst die jeweiligen AMD Module. Siehe HIER

CommonJS (CJS)

CJS ladet JavaScript Module synchron, im Gegensatz zu AMD.

//importing 
const doSomething = require('./doSomething.js'); 

//exporting
module.exports = function doSomething(n) {
  // do something
}

Primär wird CJS nur in Node.js verwendet – und damit serverseitiges JavaScript. Hier bauen Module auf dem Befehl „require“ und „module.exports“ auf.

CJS funktioniert NICHT im Browser.

Universal Module Definition (UMD)

Nach einiger Zeit waren sowohl AMD als auch CJS in allgemeiner Verwendung, jedoch nicht miteinander kompatibel.

Daher wurde UMD entwickelt welches für beide Welten (AMD und CJS) und damit sowohl Frontend als auch Backend verwendbar ist.

(function (root, factory) {
    if (typeof define === "function" && define.amd) {
        define(["jquery", "underscore"], factory);
    } else if (typeof exports === "object") {
        module.exports = factory(require("jquery"), require("underscore"));
    } else {
        root.Requester = factory(root.$, root._);
    }
}(this, function ($, _) {
    // this is where I defined my module implementation

    var Requester = { // ... };

    return Requester;
}));

Jedoch wird UMD aktuell nur mehr als Fallback verwendet. Siehe nächstes Kapitel warum.

ES Modules (ESM – empfohlene Variante)

ES Modules sind die „offizielle“ Art und Weiße laut dem ES6 Standard wie man JavaScript Module schreiben soll.

// in index.html
<script type="module" src="main.js"></script>

// in main.js
import {addTextToBody} from './util.js';
addTextToBody('Modules are pretty cool.');

// in util.js
export function addTextToBody(text) {
  const div = document.createElement('div');
  div.textContent = text;
  document.body.appendChild(div);
}

Sozusagen die Funktionalität von AMD (asynchrones laden) mit der Schreibweise von CJS.

Hier ein Beispiel mit einem „direkten“ laden von einem Module als auch einem asynchronen Laden eines Modules nach 5 Sekunden.

// index.html
<html>
    <head>
        <script type="module" src="main.js"></script>
    </head>
    <body></body>
</html>

// main.js
import {addTextToBody} from './util.js';
addTextToBody('Modules are pretty cool.');

setTimeout(function(){
    import('./later.js')
    .then(module => {
      module.laterFuncton();
    })
}, 5000);

// util.js
export function addTextToBody(text) {
    const div = document.createElement('div');
    div.textContent = text;
    document.body.appendChild(div);
}

// later.js
export function laterFuncton() {
    alert("hello");
}

Browser-Support: https://caniuse.com/#feat=es6-module

Source: https://dev.to/iggredible/what-the-heck-are-cjs-amd-umd-and-esm-ikm

Was ist JavaScript?

JavaScript (JS) ist eine Skript-Sprache welche sowohl im Browser für interaktive Elemente als auch für Server-Logik verwendet wird.

Versionen von JavaScript

Der offizielle Name von JavaScript ist „ECMAScript“.

Ab 2015 wurde der offizielle nicht mehr mit der Versions-Nummer angeführt sondern mit dem Jahr an dem die Version veröffentlicht wurde.

D.h. ES6 = ECMAScript 2015 | ES7 = ECMAScript 2016 etc.

VerOffizieller NameBeschreibung
1ECMAScript 1 (1997)Erste Edition
2ECMAScript 2 (1998)Nur redaktionelle Änderungen
3ECMAScript 3 (1999)Regular Expressions hinzugefügt
try/catch hinzugefügt
4ECMAScript 4Wurde nie veröffentlicht
5ECMAScript 5 (2009) (ES5)„strict mode“ hinzugefügt
JSON support hinzugefügt
String.trim() hinzugefügt
Array.isArray() hinzugefügt
Array Iteration Methoden hinzugefügt
5.1ECMAScript 5.1 (2011)Nur redaktionelle Änderungen
6ECMAScript 2015 (ES6)let und const hinzugefügt
Default parameter hinzugefügt
Array.find() hinzugefügt
Array.findIndex() hinzugefügt
7ECMAScript 2016 (ES7)Exponential operator (**) hinzugefügt
Array.prototype.includes.hinzugefügt
8ECMAScript 2017 (ES8)String padding hinzugefügt
Neue Object properties hinzugefügt
Async functions hinzugefügt
Shared Memory hinzugefügt
9ECMAScript 2018 (ES9)rest / spread properties hinzugefügt
Asynchronous iteration hinzugefügt
Promise.finally() hinzugefügt
Weitere RegExp hinzugefügt
10ECMAScript 2019 (ES10)Array.flat() hinzugefügt
Object.fromEntries() hinzugefügt
String.trimStart() & trimEnd() hinzugefügt
Symbol.description hinzugefügt
11ECMAScript 2020 (ES11)BigInt Typ hinzugefügt
globalThis hinzugefügt
Nullish Coalescing Operator (??) hinzugefügt
Optional Chaining Operator (?.) hinzugefügt

Diese Versionen sind aber „nur“ Vorgaben wie die Programmiersprache sich verhalten soll. Die jeweilige Implementation der Vorgaben bieten folgende JS Engines an:

  • V8 ist die weit verbreitetste Implementation von JS und wird in folgenden Sprachen und Programmen verwendet:
    • Chrome/Chromium
    • Node.js und Deno
    • Edge
  • SpiderMonkey wird in Firefox verwendet
  • Nitro wird in Safari verwendet
  • Chakra wird in Internet-Explorer verwendet

Browser Support (ES6)

BrowserVersionDatum
Chrome51Mai 2016
Firefox54Juni 2017
Edge14Aug 2016
Safari10Sep 2016
Opera38 Juni 2016

Nur Internet-Explorer ist der einzige Browser, der nicht ES6 unterstützt.

Clientseitiges JS (Browser)

Clientseitiges JS wird auf dem jeweiligen Gerät ausgeführt auf dem die Webseite aufgerufen wird.

Im Browser wird JS verwendet um z.B. das DOM ohne ein neues laden der Seite anzupassen.

document.addEventListener("DOMContentLoaded", function() {
  function createParagraph() {
    let para = document.createElement('p');
    para.textContent = 'You clicked the button!';
    document.body.appendChild(para);
  }

  const buttons = document.querySelectorAll('button');

  for(let i = 0; i < buttons.length ; i++) {
    buttons[i].addEventListener('click', createParagraph);
  }
});

Hier wird über das JS Object „document“ auf das DOM der Webseite zugegriffen und weitere DOM-Elemente hinzugefügt.

Jedoch bietet der Browser andere APIs ebenso an wie z.b.

  • Geo Location (navigator.geolocation) um den aktuellen Standort des Gerätes zu erhalten
  • WebGL und Canvas API um komplizierte Animationen und 3D Elemente zu erstellen
  • Audio & Video API

Serverseitiges JS (Node.js)

Serverseitiges JS wird vom Server ausgeführt um z.B. Text an einen Client zu senden.

Jedoch gibt es diverse Frameworks welche die Erstellung von Webseiten für sowohl den Client als auch den Server zu erleichtern.

Für mehr Informationen über Node.js siehe HIER.

Source:
https://www.w3schools.com/js/js_versions.asp
https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps/What_is_JavaScript