Start · Sprachen · JavaScript
JS

JavaScript

ECMAScript 2024 · seit 1995 · Web

Die Sprache des Web-Frontends — läuft in jedem Browser, mit Node.js auch auf dem Server. Von einfachen DOM-Manipulationen bis zu komplexen SPAs mit React, Vue oder Svelte.

Was ist JavaScript?

JavaScript ist die Programmiersprache des Webs: Sie läuft in jedem Browser und — über Node.js, Deno oder Bun — auch serverseitig. 1995 bei Netscape entstanden, ist sie heute die am häufigsten verwendete Sprache weltweit und Pflichtwissen für jede Form von Frontend-Entwicklung.

event-driven functional imperative object-oriented prototype-based

// Steckbrief

Dateiendung
js
Paket-Mgr
npm
Offiziell
tc39.es
Docs
developer.mozilla.org/de/docs/Web/JavaScript
📚
8880 Einträge in der JavaScript-Referenz
Jede Funktion, Klasse und jedes Sprach-Konzept auf einer eigenen Seite — mit Signatur, Parametern, Beispielen und Fallstricken.
→

Übersicht

JavaScript ist die Programmiersprache des Web-Frontends. Sie läuft in jedem modernen Browser und — dank Node.js — auch auf dem Server. Ursprünglich 1995 in nur 10 Tagen von Brendan Eich bei Netscape entwickelt, ist JavaScript heute die verbreitetste Programmiersprache weltweit.

Historie

JavaScript wurde 1995 als „Mocha" bei Netscape entwickelt, umbenannt zu „LiveScript" und schließlich zu „JavaScript" — obwohl es technisch nichts mit Java zu tun hat. Die Standardisierung erfolgt seit 1997 als ECMAScript durch die ECMA International. Neue Versionen (ES2015 aka ES6, ES2020, ES2024 …) erweitern die Sprache jährlich.

Warum JavaScript?

JavaScript ist die einzige Sprache, die nativ in jedem Browser läuft. Für alles Interaktive im Web führt kein Weg an JavaScript (oder TypeScript, das zu JS kompiliert) vorbei. Mit Node.js ist JavaScript auch auf dem Server ein First-Class-Citizen und ermöglicht Full-Stack-Entwicklung in einer einzigen Sprache.

Installation

JavaScript benötigt keine Installation — es läuft in jedem modernen Browser. Für Server-seitige Entwicklung: Node.js von nodejs.org installieren.

Hello World

Das minimalste vollständige Programm in JavaScript:

console.log('Hello, World!');

Variablen & Datentypen

↗ Eigene Seite

Diese Sektion zeigt, wie du Variablen mit const, let und var deklarierst, welche Datentypen JavaScript kennt und wie sich Primitive von Objekt-Referenzen unterscheiden.

Variablen speichern Werte unter einem Namen. In modernem JavaScript deklarierst du sie mit const oder let. var findest du fast nur noch in älterem Code. Den Datentyp hat der Wert, nicht die Variable, denn JS ist dynamisch typisiert.

const, let und var

Mit const kannst du einer Variablen keinen neuen Wert zuweisen, mit let schon. Beide gelten nur innerhalb ihres Blocks. var gilt in der ganzen Funktion und wird gehoisted. Das führt oft zu Fehlern.

const max = 10;
let count = 0;
count += 1;      // ok
// max = 20;     // TypeError

if (true) {
  let inner = 'nur hier';
}
// inner         // ReferenceError

Best Practice: Nimm standardmäßig const. let brauchst du nur, wenn du wirklich neu zuweist.

Primitive Datentypen

Es gibt sieben primitive Typen: string, number, bigint, boolean, undefined, null und symbol. Primitive sind unveränderlich und werden als Wert kopiert.

const name = `Ada`;
const pi = 3.14;
const big = 9007199254740993n;
const aktiv = true;
let leer;            // undefined
const nichts = null;
const id = Symbol('id');

Typen prüfen mit typeof

Der Operator typeof gibt den Typ als String zurück. Bei null und Arrays brauchst du eigene Prüfungen.

typeof 'Hallo';    // 'string'
typeof 42;         // 'number'
typeof undefined;  // 'undefined'
typeof null;       // 'object' (historischer Bug!)
typeof [];         // 'object'
typeof (() => {}); // 'function'

Array.isArray([]); // true
value === null;    // korrekte null-Prüfung

Objekte und Referenzen

Alles, was kein Primitiv ist, ist ein Objekt. Das gilt auch für Arrays und Funktionen. In der Variablen steht dann eine Referenz auf das Objekt. const schützt nur die Bindung, nicht den Inhalt.

const user = { name: 'Ada' };
const alias = user;
alias.name = 'Grace';
console.log(user.name); // 'Grace'

const kopie = { ...user };          // flache Kopie
const tief = structuredClone(user); // tiefe Kopie

Fallstrick: Object.freeze() friert nur die oberste Ebene ein. Verschachtelte Objekte kannst du weiterhin ändern.

Operatoren & Ausdrücke

↗ Eigene Seite

Diese Sektion erklärt die wichtigsten Operatoren in JavaScript: Arithmetik, Vergleiche, logische und Nullish-Operatoren, Optional Chaining und Zuweisungskurzformen. Außerdem geht es darum, wie Ausdrücke ausgewertet werden.

Ein Ausdruck ist jedes Stück Code, das einen Wert ergibt, zum Beispiel 2 + 3, user.name oder fn(). Operatoren verknüpfen Werte zu neuen Ausdrücken. Wer ihre Regeln kennt, besonders bei Typumwandlung und Kurzschlussauswertung, vermeidet viele typische Bugs.

Arithmetik und Zuweisung

Neben + - * / gibt es % (Rest) und ** (Potenz). Vorsicht: + verkettet Strings, sobald einer der Operanden ein String ist.

const a = 7 % 3;      // 1
const b = 2 ** 10;    // 1024
const c = '5' + 1;    // '51' (String!)
const d = '5' - 1;    // 4 (Zahl)

let count = 0;
count += 5;           // 5
count++;              // 6

Vergleiche: === statt ==

== wandelt Typen vor dem Vergleich um und liefert dadurch überraschende Ergebnisse. Nimm standardmäßig === und !==. Objekte werden über ihre Referenz verglichen, nicht über ihren Inhalt.

0 == '';          // true  (Typumwandlung)
0 === '';         // false
null == undefined; // true
[1] === [1];      // false (verschiedene Referenzen)
Number.isNaN(NaN); // true – NaN === NaN ist false

Logische Operatoren, ?? und ?.

&& und || werten per Kurzschluss aus und geben einen der Operanden zurück, nicht zwingend true oder false. ?? greift nur bei null oder undefined. Dadurch bleiben gültige Werte wie 0 oder '' erhalten. ?. bricht den Zugriff ab, wenn ein Zwischenwert fehlt, und ergibt dann undefined.

const config = { retries: 0 };

const r1 = config.retries || 3;  // 3 – 0 ist falsy
const r2 = config.retries ?? 3;  // 0 – korrekt

const city = user?.address?.city ?? 'unbekannt';
const result = api.fetch?.();    // nur aufrufen, wenn vorhanden

// Logische Zuweisung (ES2021)
config.timeout ??= 5000;
isReady &&= checkState();

Hinweis: ?? darf nicht ohne Klammern mit && oder || kombiniert werden, sonst gibt es einen SyntaxError.

Ternärer Operator und Rangfolge

bedingung ? a : b ist ein Ausdruck und passt deshalb in Template Literals oder Rückgabewerte. Verschachtelte Ternaries werden schnell unleserlich. Im Zweifel lieber if verwenden oder mit Klammern arbeiten.

const label = `${items.length} ${items.length === 1 ? 'Eintrag' : 'Einträge'}`;

const x = 2 + 3 * 4;     // 14 – * vor +
const y = (2 + 3) * 4;   // 20
const z = typeof null;   // 'object' (historischer Fehler)

Best Practice: Setze Klammern, wo die Rangfolge nicht sofort klar ist. Sie kosten nichts und machen die Absicht eindeutig.

Kontrollstrukturen

↗ Eigene Seite

Diese Sektion behandelt Verzweigungen (if, switch) und Schleifen (for, for...of, for...in, while) in JavaScript, dazu break, continue und den Einsatz von await in Schleifen.

Kontrollstrukturen legen fest, welche Anweisungen wann und wie oft ausgeführt werden. In JavaScript sind das vor allem Verzweigungen (if, switch) und Schleifen (for, while, for...of).

if / else

Die Bedingung wird in einen Boolean umgewandelt. 0, "", null, undefined und NaN gelten als falsy.

const alter = 17;
if (alter >= 18) {
  console.log('volljährig');
} else if (alter >= 16) {
  console.log('fast');
} else {
  console.log('minderjährig');
}

switch

switch vergleicht strikt (===). Ohne break läuft die Ausführung in den nächsten case weiter (Fall-through).

const status = 404;
switch (status) {
  case 200:
    console.log('OK');
    break;
  case 401:
  case 403:
    console.log('Kein Zugriff');
    break;
  default:
    console.log(`Status ${status}`);
}

Schleifen

Für Arrays und andere Iterables ist for...of die erste Wahl. for...in iteriert über die Schlüssel eines Objekts, auch über geerbte. Für Arrays ist es deshalb ungeeignet.

const farben = ['rot', 'grün', 'blau'];
for (const [i, farbe] of farben.entries()) {
  console.log(i, farbe);
}

const user = { name: 'Ada', rolle: 'admin' };
for (const key in user) {
  console.log(key, user[key]);
}

while prüft die Bedingung vor jedem Durchlauf, do...while erst danach. Dort läuft der Rumpf also mindestens einmal.

break, continue und await

break beendet die Schleife, continue springt zum nächsten Durchlauf. Anders als bei forEach funktioniert await in echten Schleifen und arbeitet die Schritte nacheinander ab.

const urls = ['/a', '/b', '/c'];
for (const url of urls) {
  const res = await fetch(url);
  if (!res.ok) continue;
  const data = await res.json();
  if (data.done) break;
}

Tipp: Deklariere Schleifenvariablen mit let oder const. Dann bekommt jeder Durchlauf eine eigene Bindung, und typische Closure-Fehler bleiben aus.

Schleifen & Iteration

↗ Eigene Seite

Diese Sektion zeigt die Schleifenformen in JavaScript: for, while, for…of, for…in und for await…of. Dazu kommen break, continue und Labels sowie typische Fallstricke.

Welche Schleife passt, hängt davon ab, worüber du iterierst: über Indizes, über die Werte eines Iterables, über die Schlüssel eines Objekts oder über asynchrone Daten.

for und while

Die klassische for-Schleife nimmst du, wenn du den Index brauchst. while prüft die Bedingung vor jedem Durchlauf. do…while läuft mindestens einmal.

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

let n = 3;
while (n > 0) n--;

Verwende let statt var. Nur mit let bekommt jede Iteration eine eigene Bindung. Das ist wichtig bei Closures, zum Beispiel in setTimeout.

for…of vs. for…in

for…of durchläuft die Werte von Iterables wie Array, String, Map, Set oder NodeList. for…in durchläuft die aufzählbaren Schlüssel eines Objekts, auch geerbte. Für Arrays ist for…in deshalb ungeeignet.

const farben = ['rot', 'grün'];
for (const [i, farbe] of farben.entries()) {
  console.log(`${i}: ${farbe}`);
}

const user = { name: 'Ada', rolle: 'admin' };
for (const [key, value] of Object.entries(user)) {
  console.log(`${key} = ${value}`);
}

Object.entries() zusammen mit for…of ist meist sauberer als for…in, weil nur eigene Eigenschaften vorkommen.

break, continue und Labels

break beendet die Schleife. continue springt direkt zur nächsten Iteration. Mit einem Label kannst du auch verschachtelte Schleifen gezielt verlassen.

aussen: for (const zeile of matrix) {
  for (const zelle of zeile) {
    if (zelle === null) continue;
    if (zelle === 0) break aussen;
  }
}

Achtung: forEach() kennt kein break. Wenn du vorzeitig abbrechen willst, nimm for…of, some() oder find().

Asynchron: await in Schleifen

await in for…of arbeitet die Schritte nacheinander ab. Für asynchrone Iterables wie Streams oder Async-Generatoren gibt es for await…of.

async function* zaehler() {
  yield 1;
  yield 2;
}
for await (const wert of zaehler()) console.log(wert);

// parallel statt nacheinander:
const daten = await Promise.all(urls.map((u) => fetch(u)));

Ein häufiger Fehler: await in einem forEach()-Callback wartet nicht. Die Schleife läuft sofort weiter.

Funktionen & Closures

↗ Eigene Seite

Diese Sektion behandelt die Syntaxformen von Funktionen in JavaScript, also Deklaration, Ausdruck und Arrow Function, dazu Parameter-Features und Closures. Außerdem geht es um typische Fallstricke mit Gültigkeitsbereich und this.

In JavaScript sind Funktionen normale Werte. Du kannst sie Variablen zuweisen, als Argument übergeben und aus anderen Funktionen zurückgeben. Eine Closure entsteht, wenn eine Funktion Variablen aus dem Bereich verwendet, in dem sie definiert wurde. Diese Variablen bleiben erhalten, auch wenn die äußere Funktion schon beendet ist.

Deklaration, Ausdruck, Arrow Function

Funktionsdeklarationen werden gehoistet. Du kannst sie also schon vor ihrer Definition aufrufen. Bei Funktionsausdrücken und Arrow Functions geht das nicht. Arrow Functions haben außerdem kein eigenes this und kein arguments.

function add(a, b) { return a + b; }
const mul = function (a, b) { return a * b; };
const square = (x) => x * x;          // impliziter Rückgabewert
const toObj = (id) => ({ id });       // Objektliteral in Klammern!

Parameter: Defaults, Rest, Destrukturierung

Standardwerte greifen nur bei undefined, nicht bei null. Rest-Parameter sammeln die übrigen Argumente in einem echten Array.

const greet = (name = 'Welt', ...rest) =>
  `Hallo ${name}! (${rest.length} weitere)`;

const connect = ({ host = 'localhost', port = 80 } = {}) =>
  `${host}:${port}`;

connect({ port: 8080 }); // "localhost:8080"
connect();               // "localhost:80"

Closures

Die innere Funktion behält eine Referenz auf die Variablen der äußeren, keine Kopie. So lässt sich privater Zustand ohne Klassen umsetzen:

const createCounter = (start = 0) => {
  let count = start;
  return {
    inc: () => ++count,
    reset: () => { count = start; },
  };
};

const counter = createCounter();
counter.inc(); counter.inc(); // 2
// count ist von außen nicht erreichbar

Closures sind auch die Grundlage von Callbacks, Event-Handlern, Memoisierung und Currying.

Fallstricke

  • var in Schleifen: Alle Closures teilen sich dieselbe Variable. Mit let bekommt jeder Durchlauf eine eigene Bindung.
  • Arrow Functions als Methoden: this zeigt dann nicht auf das Objekt. Nutze dafür die Methodenkurzschreibweise obj = { run() { … } }.
  • Speicher: Closures halten referenzierte Objekte am Leben. Entferne Event-Listener, die du nicht mehr brauchst.
for (var i = 0; i < 3; i++) setTimeout(() => console.log(i)); // 3 3 3
for (let j = 0; j < 3; j++) setTimeout(() => console.log(j)); // 0 1 2

Arrays & Iterables

↗ Eigene Seite

Diese Sektion erklärt, wie du Arrays erzeugst, transformierst und destrukturierst und wie das Iterator-Protokoll funktioniert, auf dem for…of, Spread und Co. aufbauen.

Arrays sind geordnete Listen beliebiger Werte. Iterables sind das allgemeinere Konzept dahinter: Alles, was Symbol.iterator implementiert, also Arrays, Strings, Map und Set, kannst du mit for…of, Spread und Destrukturierung verarbeiten.

Arrays erzeugen

const nums = [1, 2, 3];
const gerade = Array.from({ length: 3 }, (_, i) => i * 2); // [0, 2, 4]
const chars = Array.from('abc'); // ['a', 'b', 'c']
const eins = Array.of(7); // [7]

Fallstrick: new Array(7) erzeugt kein Array mit der Zahl 7, sondern ein Array mit 7 leeren Plätzen.

Transformieren statt mutieren

map, filter und reduce geben neue Werte zurück und verändern das Original nicht. Seit ES2023 gibt es außerdem nicht-mutierende Varianten: toSorted, toReversed, toSpliced und with.

const users = [{ name: 'Ada', age: 36 }, { name: 'Linus', age: 28 }];
const namen = users.filter(({ age }) => age > 30).map(({ name }) => name);
const summe = users.reduce((acc, u) => acc + u.age, 0);
const sortiert = users.toSorted((a, b) => a.age - b.age);

sort() verändert das Original und vergleicht ohne Vergleichsfunktion als Strings: [10, 9, 1].sort() ergibt [1, 10, 9]. Gib deshalb immer eine Vergleichsfunktion mit.

Spread und Destrukturierung

const [first, , third, ...rest] = [1, 2, 3, 4, 5];
const kopie = [...nums, 4]; // flache Kopie
const unique = [...new Set([1, 1, 2, 3])]; // [1, 2, 3]
const letztes = nums.at(-1); // 3

Das Iterator-Protokoll

Ein Objekt ist iterierbar, wenn [Symbol.iterator]() einen Iterator zurückgibt. Dessen next() liefert jeweils { value, done }. Am einfachsten baust du so etwas mit Generatoren.

const range = (start, end) => ({
  *[Symbol.iterator]() {
    for (let i = start; i < end; i++) yield i;
  },
});
for (const n of range(1, 4)) console.log(n); // 1, 2, 3
const arr = [...range(0, 3)]; // [0, 1, 2]

Tipp: for…in durchläuft Schlüssel, auch geerbte. Für die Werte eines Arrays nimmst du for…of. Für asynchrone Quellen wie Streams gibt es for await…of.

Strings & Template Literals

↗ Eigene Seite

Diese Sektion zeigt die drei String-Schreibweisen in JavaScript, Interpolation und mehrzeilige Texte mit Template Literals, Tagged Templates und die wichtigsten String-Methoden.

Strings sind in JavaScript unveränderliche Zeichenfolgen in UTF-16. Du schreibst sie mit einfachen oder doppelten Anführungszeichen oder mit Backticks. Backticks erzeugen Template Literals. Damit kannst du Werte direkt in den Text einsetzen und über mehrere Zeilen schreiben. Sobald Werte in den String sollen, sind sie heute die erste Wahl.

Drei Schreibweisen

const a = 'einfach';
const b = "doppelt";
const c = `Backtick`;
const pfad = 'C:\\temp\\datei.txt'; // Backslash escapen

' und " funktionieren genau gleich. Nutze im ganzen Projekt eine Variante und lass sie von Prettier oder ESLint durchsetzen.

Interpolation und Mehrzeiligkeit

In ${…} darf jeder beliebige Ausdruck stehen. Sein Ergebnis wird in einen String umgewandelt.

const user = { name: 'Lea', punkte: 42 };
const text = `${user.name} hat ${user.punkte * 2} Punkte.`;

const liste = `
  <ul>
    ${['A', 'B'].map((x) => `<li>${x}</li>`).join('')}
  </ul>`;

Fallstricke: Einrückungen und Zeilenumbrüche stehen genau so im String, wie du sie tippst. Objekte werden zu [object Object], dafür brauchst du JSON.stringify(). Setz Nutzereingaben nie ungeprüft in HTML ein, sonst droht XSS.

Tagged Templates

Steht eine Funktion direkt vor dem Backtick, bekommt sie die festen Textteile und die eingesetzten Werte getrennt übergeben. Das ist nützlich für Escaping, Übersetzungen (i18n) oder SQL-Builder.

const safe = (strings, ...values) =>
  strings.reduce((out, str, i) => {
    const v = String(values[i - 1] ?? '')
      .replace(/[<>&]/g, (ch) => `&#${ch.charCodeAt(0)};`);
    return out + v + str;
  });

const eingabe = '<img onerror=alert(1)>';
safe`Hallo ${eingabe}!`; // 'Hallo &#60;img onerror=alert(1)&#62;!'

String.raw`C:\temp\neu`; // Backslashes bleiben erhalten

Wichtige Methoden

const s = '  JavaScript  ';
s.trim();                     // 'JavaScript'
s.includes('Script');         // true
'abc'.at(-1);                 // 'c'
'7'.padStart(3, '0');         // '007'
'a-b-c'.replaceAll('-', '/'); // 'a/b/c'
'a,b'.split(',');             // ['a', 'b']

Jede dieser Methoden gibt einen neuen String zurück, das Original bleibt unverändert. length zählt UTF-16-Einheiten, deshalb zählen viele Emojis doppelt. Die tatsächlich sichtbaren Zeichen zählst du mit [...str].length.

Objekte & Prototypen

↗ Eigene Seite

Diese Sektion erklärt, wie Objekte in JavaScript aufgebaut sind, wie die Prototypenkette funktioniert und warum `class` nur eine lesbarere Schreibweise für Prototypen ist.

Objekte sind in JavaScript Sammlungen von Schlüssel-Wert-Paaren. Vererbung funktioniert über Prototypen: Jedes Objekt verweist intern ([[Prototype]]) auf ein anderes Objekt und erbt dessen Eigenschaften. Wer das Prinzip kennt, versteht auch class, this und die Suche nach Methoden.

Objekt-Literale

const user = {
  name: 'Ada',
  age: 36,
  greet() {
    return `Hallo, ${this.name}`;
  },
};

const { name, ...rest } = user;
const copy = { ...user, age: 37 };

Der Spread-Operator erstellt nur eine flache Kopie. Für tiefe Kopien gibt es structuredClone().

Die Prototypenkette

Fehlt eine Eigenschaft am Objekt selbst, sucht die Engine im Prototyp weiter. Das geht so lange, bis sie null erreicht.

const animal = {
  speak() { return `${this.name} macht ein Geräusch`; },
};

const dog = Object.create(animal);
dog.name = 'Rex';

dog.speak();                           // 'Rex macht ein Geräusch'
Object.getPrototypeOf(dog) === animal; // true
Object.hasOwn(dog, 'speak');           // false

__proto__ ist veraltet. Nutze stattdessen Object.getPrototypeOf() und Object.setPrototypeOf(). Den Prototyp zur Laufzeit zu ändern macht den Code allerdings langsam.

Klassen als Syntax-Zucker

class baut dieselbe Prototypenstruktur auf, nur lesbarer. Methoden landen auf Klasse.prototype.

class Animal {
  #id = crypto.randomUUID(); // privates Feld
  constructor(name) { this.name = name; }
  speak() { return `${this.name} macht ein Geräusch`; }
}

class Dog extends Animal {
  speak() { return `${super.speak()} – Wuff!`; }
}

const rex = new Dog('Rex');
rex instanceof Animal; // true
Object.getPrototypeOf(Dog.prototype) === Animal.prototype; // true

Fallstricke

  • for...in durchläuft auch geerbte Eigenschaften. Besser sind Object.keys() oder Object.entries().
  • Arrow Functions als Methoden haben kein eigenes this.
  • Object.create(null) erzeugt ein Objekt ohne Prototyp. Das eignet sich als reines Dictionary. Oft ist aber Map die bessere Wahl.

Klassen (ES6+)

↗ Eigene Seite

Diese Sektion zeigt die Klassensyntax ab ES2015: Konstruktor, Methoden, Getter, statische und private Member, Vererbung mit extends/super sowie typische Fallstricke rund um this.

Seit ES2015 definiert man Objekte mit gemeinsamem Verhalten meist über Klassen. Intern arbeitet JavaScript weiterhin mit Prototypen. class ist eine klarere und strengere Schreibweise für Konstruktorfunktionen.

Grundaufbau

class User {
  role = 'gast';     // öffentliches Feld
  #passwort;         // privates Feld

  constructor(name, passwort) {
    this.name = name;
    this.#passwort = passwort;
  }

  get anzeigeName() {
    return `${this.name} (${this.role})`;
  }

  pruefe(eingabe) {
    return eingabe === this.#passwort;
  }

  static gast() {
    return new User('Gast', '');
  }
}

const u = new User('Ada', 'geheim');
u.anzeigeName;      // 'Ada (gast)'
u.pruefe('geheim'); // true

Methoden liegen auf User.prototype, Felder auf jeder Instanz. Auf #-Felder kann man von außen tatsächlich nicht zugreifen. Ein Versuch führt schon beim Parsen zu einem SyntaxError.

Vererbung mit extends und super

class Admin extends User {
  role = 'admin';

  constructor(name, passwort, rechte = []) {
    super(name, passwort); // vor jedem this-Zugriff
    this.rechte = rechte;
  }

  get anzeigeName() {
    return `★ ${super.anzeigeName}`;
  }
}

new Admin('Linus', 'root') instanceof User; // true

In abgeleiteten Klassen muss super() aufgerufen werden, bevor this verwendet wird. Sonst gibt es einen ReferenceError. Auch eingebaute Typen wie Error oder Array lassen sich erweitern.

this in Callbacks

Methoden sind nicht automatisch an ihr Objekt gebunden. Übergibt man eine Methode als Callback, geht this verloren. Abhilfe schafft eine Arrow-Function als Klassenfeld:

class Zaehler {
  wert = 0;
  erhoehe = () => { this.wert++; };
}

const z = new Zaehler();
button.addEventListener('click', z.erhoehe);

Der Nachteil: Arrow-Felder werden für jede Instanz neu erzeugt und liegen nicht auf dem Prototyp.

Fallstricke

  • Klassen werden nicht wie Funktionen gehoistet. Wer sie vor der Deklaration nutzt, bekommt einen ReferenceError (TDZ).
  • Der Klassenkörper läuft immer im Strict Mode.
  • Ein Aufruf ohne new wirft einen TypeError.
  • Private Member und static-Blöcke gibt es seit ES2022. Alle aktuellen Browser unterstützen sie.

Module (import/export)

↗ Eigene Seite

ES-Module teilen JavaScript-Code in Dateien mit eigenem Gültigkeitsbereich auf. Diese Sektion behandelt benannte und Default-Exporte, Importe, Re-Exporte, dynamisches import() und den Einsatz im Browser und in Node.js.

ES-Module (ESM) teilen Code in Dateien mit eigenem Gültigkeitsbereich auf. Was eine Datei nicht ausdrücklich exportiert, bleibt privat. Module laufen automatisch im Strict Mode und werden nur einmal ausgewertet, egal wie oft man sie importiert.

Benannte und Default-Exporte

Eine Datei kann beliebig viele benannte Exporte haben, aber höchstens einen Default-Export.

// math.js
export const PI = 3.14159;
export const add = (a, b) => a + b;
const secret = 42; // bleibt privat

export default function multiply(a, b) {
  return a * b;
}

Importieren

import multiply, { PI, add as sum } from './math.js';
import * as math from './math.js';

console.log(sum(2, 3));      // 5
console.log(math.PI);        // 3.14159
console.log(multiply(2, 4)); // 8

Importe sind Live-Bindings: Ändert das Quellmodul einen exportierten let-Wert, sieht das importierende Modul den neuen Wert. Selbst neu zuweisen kann es ihn aber nicht. Import-Deklarationen werden gehoistet und müssen auf oberster Ebene stehen.

Re-Exporte

So bündelt eine „Barrel“-Datei mehrere Module zu einer gemeinsamen Schnittstelle:

// index.js
export { add, PI } from './math.js';
export { default as multiply } from './math.js';
export * from './strings.js'; // ohne Default-Export!

Dynamischer Import

import() ist ein Ausdruck und hat als Rückgabewert ein Promise. Das eignet sich für Code-Splitting oder für Module, die nur unter bestimmten Bedingungen geladen werden sollen.

const loadChart = async () => {
  const { default: Chart } = await import('./chart.js');
  return new Chart('#canvas');
};

Im Browser und in Node.js

  • Browser: Mit <script type="module" src="app.js"></script> wird das Skript automatisch verzögert (deferred) geladen.
  • Node.js: Dateiendung .mjs verwenden oder "type": "module" in die package.json eintragen.
  • Im Browser brauchen Pfade die Dateiendung. Paketnamen ohne Pfad wie 'lodash' funktionieren nur mit einer Import Map oder einem Bundler.
  • Top-Level-await ist nur in Modulen erlaubt.

Tipp: Benannte Exporte sind meist die bessere Wahl. Sie machen Refactoring, Autovervollständigung und Tree-Shaking einfacher.

Asynchronität (Promises, async/await)

↗ Eigene Seite

Diese Sektion erklärt, wie JavaScript mit Promises und async/await asynchron arbeitet. Behandelt werden Verkettung, Fehlerbehandlung, parallele Ausführung und typische Fallstricke.

JavaScript läuft in einem einzigen Thread. Netzwerkzugriffe und Timer blockieren ihn trotzdem nicht, weil ihre Ergebnisse später über die Event Loop geliefert werden. Ein Promise steht für einen solchen zukünftigen Wert. async/await ist die gut lesbare Syntax, mit der du Promises verarbeitest.

Promises

Ein Promise hat einen von drei Zuständen: pending (ausstehend), fulfilled (erfüllt) oder rejected (abgelehnt). Mit then, catch und finally reagierst du auf das Ergebnis. Jeder dieser Aufrufe gibt ein neues Promise zurück, deshalb kannst du sie verketten.

const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

wait(500)
  .then(() => fetch('/api/user'))
  .then((res) => res.json())
  .catch((err) => console.error(err))
  .finally(() => console.log('fertig'));

async/await

Eine async-Funktion gibt immer ein Promise zurück. await hält die Funktion an, bis das Promise erfüllt ist, und liefert dann dessen Wert. Wird das Promise abgelehnt, wirft await eine Ausnahme, die du mit try/catch abfangen kannst.

const loadUser = async (id) => {
  try {
    const res = await fetch(`/api/users/${id}`);
    if (!res.ok) throw new Error(`HTTP ${res.status}`);
    const { name } = await res.json();
    return name;
  } catch (err) {
    console.error('Laden fehlgeschlagen:', err);
    throw err;
  }
};

Achtung: fetch lehnt bei HTTP-Fehlern wie 404 nicht ab, deshalb musst du res.ok selbst prüfen. await auf oberster Ebene (Top-Level-Await) funktioniert nur in ES-Modulen.

Parallel statt nacheinander

Wenn du unabhängige Aufgaben nacheinander mit await abwartest, verlierst du Zeit. Starte sie stattdessen gemeinsam:

const [user, posts] = await Promise.all([
  fetch('/api/user').then((r) => r.json()),
  fetch('/api/posts').then((r) => r.json()),
]);
  • Promise.all: wird beim ersten Fehler abgelehnt.
  • Promise.allSettled: wartet auf alle Promises und liefert für jedes Status und Wert bzw. Fehlergrund.
  • Promise.race / Promise.any: liefert das erste abgeschlossene bzw. das erste erfolgreiche Ergebnis.

Fallstricke

  • await innerhalb von forEach wartet nicht. Nutze for...of oder Promise.all mit map.
  • Ohne await bekommst du ein Promise statt des eigentlichen Werts.
  • Unbehandelte Ablehnungen lösen unhandledrejection aus. Fange Fehler deshalb immer mit catch oder try/catch ab.

Fehlerbehandlung & Exceptions

↗ Eigene Seite

Diese Sektion zeigt, wie du in JavaScript Fehler mit try/catch/finally abfängst, mit throw eigene Exceptions auslöst und Fehler in asynchronem Code sauber behandelst.

Laufzeitfehler brechen die Ausführung ab, solange sie niemand abfängt. Mit try, catch und finally fängst du sie gezielt ab. Mit throw meldest du selbst Fehler. Das brauchst du überall, wo Eingaben, Netzwerk oder Parsing schiefgehen können.

try, catch und finally

Der catch-Block läuft nur, wenn im try-Block ein Fehler auftritt. finally läuft immer, auch nach einem return. Seit ES2019 darfst du den Parameter weglassen (optional catch binding).

const parseConfig = (json) => {
  try {
    return JSON.parse(json);
  } catch (err) {
    console.error(`Ungültiges JSON: ${err.message}`);
    return {};
  } finally {
    console.log('Parsing abgeschlossen');
  }
};

try { riskyCall(); } catch { /* Fehler bewusst ignoriert */ }

Fehler werfen und eigene Fehlerklassen

Wirf immer Error-Objekte oder Unterklassen davon, keine Strings. Nur so bekommst du message, name und einen Stacktrace. Eingebaute Typen sind z. B. TypeError, RangeError und SyntaxError. Mit der Option cause (ES2022) hängst du den ursprünglichen Fehler an.

class ValidationError extends Error {
  constructor(field, message, options) {
    super(message, options);
    this.name = 'ValidationError';
    this.field = field;
  }
}

try {
  throw new ValidationError('email', 'E-Mail fehlt');
} catch (err) {
  if (err instanceof ValidationError) {
    console.warn(`${err.field}: ${err.message}`);
  } else {
    throw err; // Unbekanntes nicht verschlucken
  }
}

Fehler in asynchronem Code

Bei await funktioniert try/catch wie bei synchronem Code. Aufgepasst: fetch wirft bei HTTP-Status 404 oder 500 keinen Fehler. Den Status musst du selbst prüfen.

const loadUser = async (id) => {
  try {
    const res = await fetch(`/api/users/${id}`);
    if (!res.ok) throw new Error(`HTTP ${res.status}`);
    return await res.json();
  } catch (err) {
    throw new Error('Benutzer konnte nicht geladen werden', { cause: err });
  }
};

loadUser(42).catch((err) => console.error(err.message, err.cause));

Best Practices

  • Fang nur Fehler ab, die du auch sinnvoll behandeln kannst. Alles andere wirfst du erneut.
  • Promises ohne await oder .catch() führen zu unhandled rejections.
  • Ein return in finally überschreibt Rückgabewerte und verschluckt Fehler. Das solltest du vermeiden.

DOM & Web-APIs

↗ Eigene Seite

Diese Sektion zeigt, wie du mit JavaScript im Browser das DOM ausliest und veränderst, auf Events reagierst und mit fetch Daten nachlädst.

Im Browser verbindet JavaScript deinen Code mit der Seite. Über das DOM (Document Object Model) liest und änderst du HTML-Elemente. Über Web-APIs wie fetch oder localStorage greifst du auf Netzwerk und Speicher zu. Diese APIs gehören nicht zur Sprache selbst. Sie kommen von der Laufzeitumgebung, deshalb fehlen sie in Node.js teilweise.

Elemente finden und verändern

querySelector gibt das erste passende Element zurück oder null, wenn nichts passt. querySelectorAll gibt eine statische NodeList zurück. Für reinen Text nimmst du besser textContent als innerHTML. So verhinderst du XSS-Lücken.

const list = document.querySelector('#todos');
const items = document.querySelectorAll('.todo');

items.forEach((el) => el.classList.toggle('done'));

const li = document.createElement('li');
li.textContent = 'Neue Aufgabe';
li.dataset.id = '42';
list?.append(li);

Events behandeln

Mit addEventListener registrierst du Handler. Bei Event Delegation genügt ein einziger Listener am Elternelement. Er greift auch für Kinder, die erst später hinzukommen. Mit einem AbortController kannst du mehrere Listener auf einmal entfernen.

const controller = new AbortController();

list.addEventListener('click', (event) => {
  const item = event.target.closest('.todo');
  if (!item) return;
  console.log(`Klick auf ${item.dataset.id}`);
}, { signal: controller.signal });

// später: alle Listener mit diesem Signal entfernen
controller.abort();

Daten laden mit fetch

fetch gibt ein Promise zurück. Achtung: HTTP-Fehler wie 404 oder 500 lassen das Promise nicht scheitern. Es wird nur bei Netzwerkfehlern abgelehnt. Prüfe deshalb response.ok selbst.

const loadUser = async (id) => {
  const res = await fetch(`/api/users/${id}`);
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  const { name, email } = await res.json();
  return { name, email };
};

Praxistipps

  • Binde Skripte mit defer oder type="module" ein. Dann ist das DOM schon fertig aufgebaut, wenn dein Code läuft.
  • localStorage speichert nur Strings und arbeitet synchron. Objekte wandelst du vorher mit JSON.stringify um.

Best Practices & Fallstricke

↗ Eigene Seite

Die häufigsten Fallstricke in JavaScript und wie du sie vermeidest: Vergleiche, Standardwerte, Mutation, asynchroner Code und Gleitkommazahlen.

Viele Bugs in JavaScript entstehen nicht durch komplexe Logik, sondern durch bekannte Eigenheiten der Sprache. Hier findest du die häufigsten Stolperfallen und wie du sie mit moderner Syntax umgehst.

Strikter Vergleich und sinnvolle Standardwerte

Nutze === statt ==, dann gibt es keine implizite Typumwandlung. Für Standardwerte ist ?? meist besser als ||, weil es 0 und '' stehen lässt.

0 == '';   // true – Überraschung
0 === '';  // false

const limit = options.limit || 10;     // 0 wird zu 10!
const safeLimit = options.limit ?? 10; // 0 bleibt 0
const city = user?.address?.city ?? 'unbekannt';

Ungewollte Mutation vermeiden

const schützt nur die Bindung, nicht den Inhalt. sort(), reverse() und splice() verändern das Original-Array. Seit ES2023 gibt es Varianten, die das Original unverändert lassen.

const nums = [10, 1, 5];
const sorted = nums.toSorted((a, b) => a - b); // nums bleibt gleich
const copy = { ...config, debug: true };      // flache Kopie
const deep = structuredClone(config);         // tiefe Kopie

Achtung: sort() ohne Vergleichsfunktion sortiert als Zeichenketten. [10, 1, 5].sort() ergibt [1, 10, 5].

Asynchronen Code richtig schreiben

forEach wartet nicht auf async-Callbacks. Nimm for...of, wenn die Schritte nacheinander laufen sollen, und Promise.all, wenn sie parallel laufen dürfen. Fang Fehler immer ab.

const loadAll = async (ids) => {
  try {
    return await Promise.all(ids.map(async (id) => {
      const res = await fetch(`/api/users/${id}`);
      if (!res.ok) throw new Error(`HTTP ${res.status}`);
      return res.json();
    }));
  } catch (err) {
    console.error('Laden fehlgeschlagen:', err);
    return [];
  }
};

Wichtig: fetch wirft bei 404 oder 500 keinen Fehler. Prüf deshalb immer res.ok.

Zahlen und Gleitkomma

Zahlen in JavaScript sind binäre Gleitkommazahlen (IEEE 754). Vergleich sie deshalb mit einer Toleranz und speicher Geldbeträge als ganze Cent-Werte.

0.1 + 0.2 === 0.3;                           // false
Math.abs(0.1 + 0.2 - 0.3) < Number.EPSILON;  // true
Number.isNaN('abc');                         // false (isNaN('abc') → true)

Code-Snippets

Kopierfertige Rezepte für den Alltag — mit Erklärung und erwartetem Output.

HTTP & Web

fetch-Wrapper mit Timeout pro Versuch und automatischen Wiederholungen mit exponentiellem Backoff bei Netzwerkfehlern, 429 und 5xx. Für instabile APIs.

const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

export async function fetchWithRetry(url, { retries = 3, timeout = 5000, backoff = 500, ...opts } = {}) {
  for (let attempt = 0; ; attempt++) {
    try {
      // Eigener Timeout pro Versuch
      const res = await fetch(url, { ...opts, signal: AbortSignal.timeout(timeout) });
      // Nur 429 und 5xx wiederholen, 4xx direkt zurückgeben
      if ((res.status === 429 || res.status >= 500) && attempt < retries) {
        throw new Error(`HTTP ${res.status}`);
      }
      return res;
    } catch (err) {
      if (attempt >= retries) throw err;
      // Exponentieller Backoff mit Jitter
      await sleep(backoff * 2 ** attempt + Math.random() * 100);
    }
  }
}

// in async-Funktion:
// const res = await fetchWithRetry('/api/data', { timeout: 3000, retries: 2 });

JSON & Serialisierung

Parst JSON ohne Exception mit Fallback-Wert und entfernt __proto__-Schlüssel. Serialisiert auch zirkuläre Objekte und BigInt, ohne abzustürzen.

// Parsen mit Fallback, entfernt __proto__/constructor (Schutz vor Prototype Pollution)
const safeParse = (text, fallback = null) => {
  if (typeof text !== 'string') return fallback;
  try {
    return JSON.parse(text, (k, v) =>
      k === '__proto__' || k === 'constructor' ? undefined : v);
  } catch {
    return fallback;
  }
};

// Serialisieren ohne TypeError bei Zyklen oder BigInt
const safeStringify = (value, space) => {
  const seen = new WeakSet(); // markiert auch mehrfach genutzte Referenzen
  try {
    return JSON.stringify(value, (k, v) => {
      if (typeof v === 'bigint') return v.toString();
      if (v && typeof v === 'object') {
        if (seen.has(v)) return '[Circular]';
        seen.add(v);
      }
      return v;
    }, space);
  } catch {
    return undefined;
  }
};

const obj = { id: 1n };
obj.self = obj;
console.log(safeStringify(obj));
console.log(safeParse('kaputt', {}));
{"id":"1","self":"[Circular]"} {}

Ein- & Ausgabe

Liest und schreibt Textdateien asynchron mit fs/promises. Das Schreiben ist atomar: Erst wird eine temporäre Datei geschrieben, dann umbenannt, damit bei einem Absturz keine halb geschriebene Datei übrig bleibt.

import { readFile, writeFile, rename, mkdir } from 'node:fs/promises';
import { dirname } from 'node:path';

// Datei lesen – gibt null zurück, wenn sie nicht existiert
export const readText = async (path) => {
  try {
    return await readFile(path, 'utf8');
  } catch (err) {
    if (err.code === 'ENOENT') return null;
    throw err;
  }
};

// Atomar schreiben: erst in Temp-Datei, dann umbenennen
export const writeText = async (path, data) => {
  await mkdir(dirname(path), { recursive: true });
  const tmp = `${path}.${process.pid}.tmp`;
  await writeFile(tmp, data, 'utf8');
  await rename(tmp, path);
};

// Verwendung (ESM, top-level await)
await writeText('./data/notes.txt', 'Erste Zeile\n');
const content = await readText('./data/notes.txt');
console.log(content);
Erste Zeile

Datum & Zeit

Formatiert ein Datum mit Intl.DateTimeFormat passend zu Sprache und Zeitzone. Formatter werden zwischengespeichert, ungültige Datumswerte lösen einen Fehler aus.

// Formatter-Erzeugung ist teuer -> pro Locale+Optionen cachen
const cache = new Map();

const formatDate = (value, { locale = 'de-DE', ...opts } = {}) => {
  const date = value instanceof Date ? value : new Date(value);
  if (Number.isNaN(date.getTime())) {
    throw new RangeError(`Ungültiges Datum: ${value}`);
  }
  const key = `${locale}|${JSON.stringify(opts)}`;
  let fmt = cache.get(key);
  if (!fmt) {
    fmt = new Intl.DateTimeFormat(locale, opts);
    cache.set(key, fmt);
  }
  return fmt.format(date);
};

const d = '2024-03-15T14:30:00Z';
console.log(formatDate(d, { dateStyle: 'long', timeZone: 'Europe/Berlin' }));
console.log(formatDate(d, {
  locale: 'en-US', dateStyle: 'full', timeStyle: 'short',
  timeZone: 'America/New_York',
}));
15. März 2024 Friday, March 15, 2024 at 10:30 AM

Krypto & Passwörter

Hasht Passwörter mit PBKDF2-SHA256 (600k Iterationen, zufälliger Salt) über die Web Crypto API und prüft sie mit zeitkonstantem Vergleich. Funktioniert im Browser und in Node.js ab Version 20.

const b64 = (b) => btoa(String.fromCharCode(...new Uint8Array(b)));
const unb64 = (s) => Uint8Array.from(atob(s), (c) => c.charCodeAt(0));

const derive = async (pw, salt, iterations) => {
  const key = await crypto.subtle.importKey('raw', new TextEncoder().encode(pw), 'PBKDF2', false, ['deriveBits']);
  return crypto.subtle.deriveBits({ name: 'PBKDF2', hash: 'SHA-256', salt, iterations }, key, 256);
};

// Format: iter$salt$hash
const hashPassword = async (pw, iter = 600_000) => {
  const salt = crypto.getRandomValues(new Uint8Array(16));
  return `${iter}$${b64(salt)}$${b64(await derive(pw, salt, iter))}`;
};

const verifyPassword = async (pw, stored) => {
  const [iter, salt, hash] = stored.split('$');
  const a = new Uint8Array(await derive(pw, unb64(salt), +iter));
  const b = unb64(hash);
  // zeitkonstanter Vergleich
  return a.length === b.length && a.reduce((d, x, i) => d | (x ^ b[i]), 0) === 0;
};
await hashPassword('geheim') // "600000$kJ3v...==$Qx8f...=" await verifyPassword('geheim', h) // true await verifyPassword('falsch', h) // false

Erzeugt einen kryptografisch sicheren, URL-tauglichen Zufallstoken (Base64url) für Session-IDs, Reset-Links oder API-Keys. Läuft im Browser und in Node.js ab Version 19.

// Browser + Node.js ≥ 19 (globalThis.crypto = Web Crypto API)
const randomToken = (bytes = 32) => {
  // Unter 128 Bit Entropie ist ein Token leichter zu erraten
  if (!Number.isInteger(bytes) || bytes < 16 || bytes > 65536) {
    throw new RangeError('bytes muss eine ganze Zahl zwischen 16 und 65536 sein');
  }
  const buf = crypto.getRandomValues(new Uint8Array(bytes));
  // Base64url: sicher für URLs und Cookies, ohne Padding
  let bin = '';
  for (const b of buf) bin += String.fromCharCode(b);
  return btoa(bin)
    .replace(/\+/g, '-')
    .replace(/\//g, '_')
    .replace(/=+$/, '');
};

console.log(randomToken());   // 256 Bit
console.log(randomToken(16)); // 128 Bit
q3V9x_Lk2Hc0sR-mT8bYpWn4ZfJ1aE6uDgKiOvN7cXw Zp4Kc_9sQm2LwX1-tRb8Ew

Reguläre Ausdrücke

Treffer samt benannten Gruppen und Position per matchAll auslesen, per Callback ersetzen und Benutzereingaben sicher escapen, bevor man sie als RegExp nutzt.

// TT.MM.JJJJ mit benannten Gruppen
const DATE_RE = /(?<d>\d{2})\.(?<m>\d{2})\.(?<y>\d{4})/g;

// Alle Treffer mit Gruppen und Index
const findDates = (text) =>
  [...text.matchAll(DATE_RE)].map(({ groups, index }) => ({ ...groups, index }));

// Ersetzen per Callback: TT.MM.JJJJ -> JJJJ-MM-TT
const toIsoDates = (text) =>
  text.replace(DATE_RE, (_, d, m, y) => `${y}-${m}-${d}`);

// Nutzereingabe als Literal suchen (Sonderzeichen escapen)
const escapeRegex = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
// Callback verhindert, dass $& / $1 im Ersatztext interpretiert werden
const replaceLiteral = (text, search, repl) =>
  text.replace(new RegExp(escapeRegex(search), 'gi'), () => repl);

const txt = 'Start 01.03.2024, Ende 15.04.2024 (Preis: 9.99$)';
console.log(findDates(txt));
console.log(toIsoDates(txt));
console.log(replaceLiteral(txt, '9.99$', '12,50 €'));
[ { d: '01', m: '03', y: '2024', index: 6 }, { d: '15', m: '04', y: '2024', index: 23 } ] Start 2024-03-01, Ende 2024-04-15 (Preis: 9.99$) Start 01.03.2024, Ende 15.04.2024 (Preis: 12,50 €)

Umgebung & Konfig

Lädt Env-Variablen typsicher und prüft sie beim Start: Pflichtwerte, Zahlen, Booleans. Fehlt eine Variable, bricht die App sofort ab. Dazu der Zugriff in Vite über import.meta.env.

// Node.js ≥ 20.6: node --env-file=.env app.js (kein dotenv nötig)
const required = (key) => {
  const val = process.env[key];
  if (val === undefined || val === '') throw new Error(`Env-Variable fehlt: ${key}`);
  return val;
};

const toInt = (key, fallback) => {
  const n = Number.parseInt(process.env[key] ?? String(fallback), 10);
  if (Number.isNaN(n)) throw new Error(`${key} ist keine Zahl`);
  return n;
};

// Beim Start einmal prüfen, danach nur noch config nutzen
export const config = Object.freeze({
  nodeEnv: process.env.NODE_ENV ?? 'development',
  port: toInt('PORT', 3000),
  dbUrl: required('DATABASE_URL'),
  debug: process.env.DEBUG === 'true',
});

// Vite (Browser): nur Variablen mit VITE_-Präfix landen im Bundle!
// const apiUrl = import.meta.env.VITE_API_URL;
// if (import.meta.env.DEV) console.log('Dev-Modus');
Wenn DATABASE_URL fehlt: Error: Env-Variable fehlt: DATABASE_URL

Strings & Regex

Macht aus einem beliebigen String einen URL-tauglichen Slug. Umlaute werden deutsch transkribiert (ä→ae, ß→ss), Akzente entfernt, Sonderzeichen ersetzt. Optional mit Maximallänge und eigenem Trennzeichen.

const UMLAUTE = { ä: 'ae', ö: 'oe', ü: 'ue', ß: 'ss', Ä: 'Ae', Ö: 'Oe', Ü: 'Ue', ẞ: 'SS' };

const slugify = (input, { maxLength = 80, separator = '-' } = {}) =>
  String(input ?? '')
    // Deutsche Umlaute zuerst ersetzen, sonst würde NFD aus ä nur ein a machen
    .replace(/[äöüßÄÖÜẞ]/g, (c) => UMLAUTE[c])
    // Übrige Akzente abtrennen und entfernen (é → e, ç → c)
    .normalize('NFD')
    .replace(/[\u0300-\u036f]/g, '')
    .toLowerCase()
    // Alles außer a-z/0-9 wird zu einem einzelnen Bindestrich
    .replace(/[^a-z0-9]+/g, '-')
    .replace(/^-+|-+$/g, '')
    .slice(0, maxLength)
    // Nach dem Kürzen keinen Bindestrich am Ende stehen lassen
    .replace(/-+$/, '')
    .replaceAll('-', separator);

console.log(slugify('Größenänderung für Übergrößen!'));
console.log(slugify('  Café & Crème brûlée  '));
console.log(slugify('Node.js 20: Was ist neu?', { separator: '_' }));
groessenaenderung-fuer-uebergroessen cafe-creme-brulee node_js_20_was_ist_neu

Sonstiges

Debounce führt eine Funktion erst aus, wenn die Aufrufe eine Weile pausieren (z. B. Suche, Resize). Throttle führt sie höchstens einmal pro Intervall aus (z. B. Scroll), der letzte Aufruf geht nicht verloren.

// Debounce: erst ausführen, wenn seit dem letzten Aufruf `wait` ms vergangen sind
const debounce = (fn, wait = 200) => {
  let t;
  const d = function (...args) {
    clearTimeout(t);
    t = setTimeout(() => fn.apply(this, args), wait);
  };
  d.cancel = () => clearTimeout(t); // z. B. beim Unmount aufrufen
  return d;
};

// Throttle: höchstens 1× pro `wait` ms, der letzte Aufruf wird nachgeholt
const throttle = (fn, wait = 200) => {
  let last = 0, t;
  const th = function (...args) {
    const run = () => { last = Date.now(); fn.apply(this, args); };
    clearTimeout(t);
    const rest = wait - (Date.now() - last);
    rest <= 0 ? run() : (t = setTimeout(run, rest));
  };
  th.cancel = () => clearTimeout(t);
  return th;
};

// Verwendung
input.addEventListener('input', debounce(e => search(e.target.value), 300));
window.addEventListener('scroll', throttle(() => updateHeader(scrollY), 100));

Referenz-Bibliothek

Jede Funktion, Klasse und jedes Sprach-Konstrukt lebt auf einer eigenen Seite — mit Signatur, Parameter-Tabelle, Beispielen und Fallstricken.

Methode

Alle 3206 →
AbortController.abort() Bricht alle asynchronen Operationen ab, die das zugehörige <code>AbortSignal</code> beobac... AbortSignal.abort() Statische Methode, die ein bereits abgebrochenes <code>AbortSignal</code> zurückgibt – ohn... AbortSignal.any() Fasst mehrere <code>AbortSignal</code>-Objekte zu einem neuen Signal zusammen, das abbrich... AbortSignal.throwIfAborted() Wirft den Abbruch-Grund (<code>signal.reason</code>), wenn das Signal abgebrochen wurde. A... AbortSignal.timeout() Statische Methode, die ein <code>AbortSignal</code> zurückgibt, das nach <code>time</code>... AnalyserNode.getByteFrequencyData() Kopiert die aktuellen Frequenzdaten (Spektrum) des <code>AnalyserNode</code> als Byte-Wert... AnalyserNode.getByteTimeDomainData() Kopiert die aktuellen Wellenform-Daten (Zeitbereich) eines <code>AnalyserNode</code> als B... AnalyserNode.getFloatFrequencyData() Kopiert die aktuellen Frequenzdaten (Spektrum) als Dezibel-Werte in ein übergebenes <code>... AnalyserNode.getFloatTimeDomainData() Kopiert die aktuelle Wellenform (Zeitbereichsdaten) des Audiosignals als Gleitkommawerte z... ANGLE_instanced_arrays.drawArraysInstancedANGLE() Rendert Primitive aus Array-Daten wie <code>gl.drawArrays()</code>, zeichnet dabei aber me... ANGLE_instanced_arrays.drawElementsInstancedANGLE() Rendert indizierte Primitive aus einem Element-Array-Buffer mehrfach (instanziert) in eine... ANGLE_instanced_arrays.vertexAttribDivisorANGLE() Legt fest, wie oft ein generisches Vertex-Attribut beim instanzierten Zeichnen weiterrückt...
Weitere 3194 Methode anzeigen →

Häufige Fragen zu JavaScript

Wofür wird JavaScript verwendet?

JavaScript steuert Interaktionen im Browser (Buttons, Formulare, Animationen), wird für Single-Page-Apps (React, Vue, Svelte) genutzt, läuft serverseitig in Node.js, treibt Build-Tools (Vite, Webpack) an und ist Basis für Cross-Platform-Apps (Electron, React Native).

Was ist der Unterschied zwischen JavaScript und TypeScript?

TypeScript ist eine Erweiterung von JavaScript mit statischer Typisierung. Jeder gültige JavaScript-Code ist auch gültiges TypeScript. TS wird vor der Ausführung zu JS kompiliert und reduziert Laufzeit-Fehler durch Typ-Checks beim Build.

Welche JavaScript-Engine ist die schnellste?

V8 (Chrome, Node.js, Bun über JavaScriptCore-Alternative), SpiderMonkey (Firefox) und JavaScriptCore (Safari, Bun) sind alle sehr performant. Für serverseitige Workloads liegen Bun und Deno in Benchmarks oft vor Node.js.

Soll ich React, Vue oder Svelte lernen?

React hat den größten Jobmarkt, Vue ist pragmatischer und schneller erlernbar, Svelte produziert den schlanksten Code und compiliert zu Vanilla-JS. Für Jobsuche: React. Für kleine Projekte oder DX: Svelte oder Vue.

Was ist ECMAScript?

ECMAScript (ES) ist der Standard, auf dem JavaScript basiert. Jährlich erscheinen neue Versionen (ES2015, ES2016, …). "JavaScript" ist die populäre Implementierung — die Features kommen aus ECMAScript.