Timing

timeout

Über setTimeout() lässt sich ein Timeout setzen

document.querySelector("#alert-on").addEventListener("click", alertOn);
document.querySelector("#alert-off").addEventListener("click", alertOff);

let timeoutId;

function alertOn() {
    timeoutId = setTimeout(function() {
        console.log(timeoutId);
        alert("Das hat 4 Sekunden gedauert");
    }, 4000);
}

Über clearTimeout() wird ein Timeout abgebrochen:

function alertOff() {
    console.log(timeoutId);
    clearTimeout(timeoutId);
}

Alle Timeouts werden beim Laden des Skripts registriert. Der Zeitpunkt des Timeout wird also stets vom Laden des Skriptes gerechnet und nicht vom vorangegangenen Timeout aus gerechnet. Funktionen kommen auf den Stack und werden beim Eintritt ihres Timeouts ausgeführt, das Skript setzt derweil fort.

Interval

Zyklisches, wiederholendes Ausführen einer Funktion.

Über setInterval() wird ein Intervall gesetzt. Hier wird die aktuelle Zeit jede Sekunde aktualisiert:

let intervalId = setInterval(function() {
    console.log("2 Sekunden später");
}, 2000);
let timeFunc = function() {
    let datum = new Date();
    let time = datum.toLocaleTimeString();
    document.querySelector("#zeit").textContent = time;
};

let timeIntervalId = setInterval(timeFunc, 1000);

Über clearInterval() wird ein Intervall abgebrochen:

document.querySelector("#interval-off").addEventListener("click", function() {
    clearInterval(intervalId);
    clearInterval(timeIntervalId);
});

RequestAnimationFrame

Über requestAnimationFrame() wird einmalig eine Animation durchgeführt. Dabei wird die Ausführung beim Render-Prozess getätigt.

let runCat = function() {
    let position = counter * counter / 8;
    counter++;
    cat.style.left = position + "px";
    if (counter < 100) {
        animationId = requestAnimationFrame(runCat)
    }
};
requestAnimationFrame(runCat);

Hint

Funktion beim Deklarieren sofort einmalig ausführen

Über IIFE (Immediately Invoked Function Expression):

(function runCat() {
    let position = counter * counter / 8;
    counter++;
    cat.style.left = position + "px";
    if (counter < 100) {
        animationId = requestAnimationFrame(runCat)
    }
})();

Über cancelAnimationFrame() wird die Animation gestoppt:

mouse.addEventListener("click", function() {
    console.log("STOP");
    cancelAnimationFrame(animationId);
});

Location-Objekt

Das window.location Objekt beinhaltet zahlreiche Informationen zum aktuellen Dokument.

// Hyperreferenz der Seite auslesen
console.log(location.href);

// queryString (<my_url>?param1=foo -> ?1=foo)
    // if current location is https://example.com
console.log(location.search);  //<empty string>
    // if current location is https://example.com?q=123
console.log(location.search);  // "?q=123"

// hashTag (<my_url>#mytag -> #mytag)
console.log(location.hash);

// Dateienpfad -> relativer Pfad der HTML
console.log(location.pathname);

// Hostname / Domain
console.log(location.hostname);

// Protokoll (http, https, file)
console.log(location.protocol);

// Seite neu laden
document.querySelector("#interval-off").addEventListener("click", function() {
    location.reload();  // evtl. mit Übergabe von true, um Cache zu leeren
})

let myBtn =document.querySelector("#alert-on");

// JS Weiterleitung (auf eine andere Seite)
myBtn.onclick = function() {
    location.assign("https://duckduckgo.com");
    // neuer History-Eintrag wird erstellt, dadurch kann Nutzer zurückkehren
};
myBtn.onclick = function() {
    location.replace("https://duckduckgo.com");
    // kein neuer History-Eintrag, daher keine Rücknavigation möglich
}
myBtn.onclick = function() {
    // location.href = "https://duckduckgo.com";
    // genauso wie 'assign', mit History-Eintrag
    location.href += "#alfa";
}

Objekt-Funktionen

Über Prototyping erhalten Kind-Objekte die Fähigkeiten des Eltern-Objekt. Das oberste Objekt aller Objekte ist das Object Objekt. Folgendes Objekt:

let user = {
    lastName:       "Jones",
    firstName:      "Jessica",
    age:            36,
    address:        "Dragonroad 66, Hell's Kitchen",
    getFullName:    function() {
                        return this.firstName + " " + this.lastName;
                    }
};

Über Object.keys() werden alle Keys aller Attribute eines gewünschten Objekts als Array zurückgegeben:

console.log(Object.keys(user));

Über Object.values() werden die Werte aller Attribute eines gewünschten Objekts als Array zurückgegeben:

console.log(Object.values(user));

Über Object.entries() werden die Keys und Werte aller Attribute eines gewünschten Objekts als Array [key, value] zurückgegeben:

console.log(Object.entries(user));

for-in Schleife

Iteriert über Keys eines Objekts:

let heroes = ["Jessica", "Luke", "Daredevil", "Elektra", "Odin"];

let user = {
    lastName:       "Jones",
    firstName:      "Jessica",
    age:            36,
    address:        "Dragonroad 66, Hell's Kitchen",
    getFullName:    function() {
                        return this.firstName + " " + this.lastName;
                    }
};

for (let index in heroes) {
    console.log(index, heroes[index]);    // 0 Jessica -> 1 Luke -> ...
}
for  (let key in user) {
    console.log(key, user[key]);   // OK: lastName Jones -> firstName Jessica -> ...
    console.log(user.key);      // NOK: undefined  --> es gibt kein Attribute 'key'
    console.log(typeof key);    // string
}

for-of Schleife

Iteriert über Values eines Objekts. Hierfür benötigt das Objekt einen Iterator, wie z.B. das Array-Objekt dies hat:

for (let value of heroes) {
    console.log(value, heroes.indexOf(value));  // Jessica 0 -> Luke 1 -> ...
}
for (let key of Object.keys(user)) {
    console.log(key);  // lastName -> firstName -> ...
}
for (let entry of Object.entries(user)) {
    console.log(entry);  // [ "lastName", "Jones" ] -> [ "firstName", "Jessica" ] -> ...
}

Objekt einfrieren

Über Object.freeze() lässt sich ein Objekt einfrieren. Wenn ein Objekt eingefroren ist, lässt es sich nicht mehr verändern (wie andere Programmiersprachen z.B. mit Enums arbeiten).

Wichtig ist, alle Referenzen zu dem eingefrorenen Objekt ebenfalls einzufrieren, da ansonsten Änderung über diese weiterhin möglich sind.

const marvelHero = {
    name:   "Jessica Jones",
    age:    36,
    status: "angry"
};

Object.freeze(marvelHero);

// marvelHero.name = "Luke Cage"; // TypeError: "name" is read-only
// delete marvelHero.name; // TypeError: property "name" is non-configurable and can't be deleted
// marvelHero.character = "ComicHero";  // TypeError: can't define property "character": Object is not extensible

Warning

Ohne den Strikten Modus, sind diese Zuweisungen trotz Freezing möglich!

Objekt duplizieren

Neue Methode von 2022. Über structuredClone() wird ein Objekt vollständig, jedoch ohne Methoden, dupliziert. Duplizierte Objekte sind nicht eingefroren:

const marvelHero = {
        name:   "Jessica Jones",
        age:    36,
        status: "angry"
    };

let clonedHero = structuredClone(marvelHero);
clonedHero.name = "Luke Cage"
console.log(clonedHero);
console.log(clonedHero.name);  // Luke Cage
console.log(marvelHero.name);  // Jessica Jones

Andere Möglichkeiten: Prototyping oder JSON (Methoden werden hier ebenfalls ignoriert).

Objekte durch Variablen erzeugen

Es gibt eine alte und neue Möglichkeit:

let cat = "Lucy",
    dog = "Momo",
    hero = "Jessica";

// ES5 (alte Variante)
let myObject = {
    cat: cat,
    dog: dog,
    hero: hero
};

let output = function(who) {
    // 'this' referenziert das Objekt auf dem die 'output'-Methode aufgerufen wird
    return who + " is " + this[who];
}

// ES6 (neue Variante)
let myNewObject = {
    cat,
    katze: cat,   // alter Weg weiterhin möglich
    dog,
    hero,
    output  // auch mit Funktionen möglich
};
console.log(myNewObject);
console.log(myNewObject.output("hero"));    // hero is Jessica
console.log(myNewObject.output("katze"));   // katze is Lucy

FormData-Object

Über ein FormData Objekt lässt sich ein Formular erzeugen

// leeres FormData-Object erstellen
let myFormDatas = new FormData();

// Hinzufügen von Einträgen
myFormDatas.append("firstName", "Jessica");

let myForm = document.querySelector("#myForm");

// Event-Handler damit Formular nicht abgesendet wird
myForm.onsubmit = function() {
    // Formulardaten übergeben
    let allFormDatas = new FormData(myForm);

    // Eintrag erweitern/ergänzen oder hinzufügen (falls nicht vorhanden)
    allFormDatas.append("Username", "jessica123");
    allFormDatas.append("firstName", "Jessica");

    // Eintrag ersetzen oder neu hinzufügen
    allFormDatas.set("password", "12345");

    // Eintrag prüfen
    if (allFormDatas.has("unsinn")) {
        // Eintrag löschen
        allFormDatas.delete("unsinn");
    }

    // löschen auch ohne vorherige Prüfung auf Vorhandenseins des Eintrags möglich
    // wenn nicht vorhanden, wird Aufruf ignoriert
    allFormDatas.delete("unsinn");

    // Iteratoren erstellen
    console.log(allFormDatas.keys());      // FormData-Iterator
    // Durch keys iterieren
    for (let key of allFormDatas.keys()) {
        console.log(key);
    }
    // Durch values iterieren
    for (let value of allFormDatas.values()) {
        console.log(value);
    }
    // Durch entries iterieren (default Iterator)
    for (let entry of allFormDatas) {
        console.log(entry);
    }
    // 1. Wert eines Keys ermitteln
    console.log(allFormDatas.get("username"));

    // alle value-Werte eines Eintrags ermitteln
    // Rückgabe = Array mit Werten einzelner Elemente
    console.log(allFormDatas.getAll("username"));

    // WICHTIG! Formular nicht absenden
    return false;

Datentyp: Symbol

Datentyp wird bei Webseiten-Programmierung kaum eingesetzt. Seit ES6 verfügbar. Symbole sind immer einzigartig - gleichen sich nicht untereinander. Sie können beim Erzeugen eine Beschreibung erhalten. Erzeugte Symbole werden im Symbolregister gespeichert.

console.log(Symbol() == Symbol());      // false
console.log(Symbol("test") == Symbol("test"));      // false

// Methode Symbol.for()
let sym1 = Symbol.for("hero.Jessy");
let sym2 = Symbol.for("hero.Jessy");
let sym3 = Symbol.for("hero.Jessica");

console.log(sym1 == sym2); // true
console.log(sym1 == sym3); // false

let object = {};
object.id = 99;

let symbolId = Symbol("id");
object[symbolId] = 111;     // Property-Access -> Variablenname als Key
object.symbolId = 42;   // Punktnotation: -> Symbol-Objekt als Key

console.log(object);
console.log(object.id);  // 99
console.log(object.symbolId);  // 42
console.log(object["symbolId"]);  // 42  ---> property Name muss bekannt sein
console.log(object[symbolId]);  // 111

// Properties, die als Symbol angelegt sind, sind nicht iterierbar
for (let key in object) {
    console.log(key);   // Symbol("id") wird NICHT ausgegeben
}

let symbol_2 = Symbol("id");
object[symbol_2] = 222;
console.log(object);  // --> Es gibt nun ZWEI Symbol("id") keys

for (let entry of Object.entries(object)) {
    console.log(entry);   // Beide Symbol("id") entries werden NICHT ausgegeben
}

Template String

Neu in ES6. Innerhalb von Backticks-Strings können Ausdrücke innerhalb von ${} Ausdrücke eingefügt werden.

let name = "Jessica";
let inYears = 30;
let age = 36;

// Bei Konsolenausgabe müssen weitere Zeilen an den Zeilenanfang, da sonst
// Einrück-Leerzeichen mit ausgeben werden
let templateString = `Hallo ${name}!
In ${inYears} wirst du ${age + inYears} Jahre alt sein!`;

// ... dies gilt NICHT für Ausgabe in Elementen, aber berücksichtigt keine
// Zeilenumbrüche (muss mit <br> Tags gemacht werden)
document.querySelector("#templStr").textContent = templateString;

console.log(templateString);