Top JavaScript Interview-Fragen 2026: Antworten mit Code-Beispielen
Top JavaScript Interview-Fragen 2026: Antworten mit Code-Beispielen
Die wichtigsten JavaScript Interview-Fragen für 2026 mit ausführlichen Antworten und Code-Beispielen. Von Grundlagen bis zu fortgeschrittenen Konzepten wie Async/Await, Closures und Performance.
Warum JavaScript-Interviews 2026 anders sind
JavaScript-Vorstellungsgespräche haben sich in den letzten Jahren deutlich gewandelt. Moderne Interviews testen nicht mehr nur syntaktisches Wissen ("Was macht typeof null?"), sondern konzentrieren sich auf tiefes Verständnis der Sprache, Problemlösungsstrategien und die Fähigkeit, sauberen, wartbaren Code zu schreiben.
In diesem Leitfaden findest du die häufigsten JavaScript-Interview-Fragen aus realen Bewerbungsprozessen bei Tech-Unternehmen im DACH-Raum und weltweit – mit ausführlichen, verständlichen Antworten.
Grundlegende Konzepte
Frage 1: Was ist der Unterschied zwischen var, let und const?
Dies ist eine klassische Einstiegsfrage, bei der Interviewer auf Detailwissen achten.
Antwort:
// var: funktionsscoped, wird gehoisted, kann neu deklariert werden
var x = 1;
var x = 2; // Kein Fehler
console.log(x); // 2
if (true) {
var blockVar = "ich bin sichtbar außerhalb";
}
console.log(blockVar); // "ich bin sichtbar außerhalb"
// let: blockscoped, wird gehoisted aber nicht initialisiert (TDZ)
let y = 1;
// let y = 2; // SyntaxError: Identifier 'y' already declared
if (true) {
let blockLet = "ich bin nur im Block sichtbar";
console.log(blockLet); // funktioniert
}
// console.log(blockLet); // ReferenceError
// const: blockscoped, muss bei Deklaration initialisiert werden
// Referenz ist unveränderlich, aber Inhalt von Objekten kann geändert werden
const obj = { name: "Alice" };
obj.name = "Bob"; // Erlaubt – wir ändern das Objekt, nicht die Referenz
// obj = {}; // TypeError: Assignment to constant variable
// Temporal Dead Zone (TDZ) – wichtig für Interviews!
// console.log(tdz); // ReferenceError (nicht undefined wie bei var)
let tdz = "ich existiere";
Wichtige Punkte für Interviewer:
varhat Funktions-Scope,letundconsthaben Block-Scopevarwird mitundefinedgehoisted,let/constexistieren in der TDZconstmacht die Referenz unveränderlich, nicht den Wert
Frage 2: Erkläre Closures in JavaScript
Closures sind eines der fundamentalsten und meistgefragten Konzepte.
Antwort:
Ein Closure entsteht, wenn eine Funktion auf Variablen aus ihrem äußeren Scope zugreift, auch nachdem dieser Scope die Ausführung beendet hat.
// Basis-Beispiel: Zähler mit Closure
function erstelleZaehler() {
let count = 0; // Diese Variable ist im Closure "eingeschlossen"
return {
erhoehen: function() {
count++;
return count;
},
verringern: function() {
count--;
return count;
},
wert: function() {
return count;
}
};
}
const zaehler = erstelleZaehler();
console.log(zaehler.erhoehen()); // 1
console.log(zaehler.erhoehen()); // 2
console.log(zaehler.verringern()); // 1
console.log(zaehler.wert()); // 1
// Praktisches Beispiel: Private Variablen
function BankKonto(initialGuthaben) {
let guthaben = initialGuthaben; // "Privat" – von außen nicht direkt zugänglich
return {
einzahlen(betrag) {
if (betrag <= 0) throw new Error("Betrag muss positiv sein");
guthaben += betrag;
return `Eingezahlt: ${betrag}€. Neues Guthaben: ${guthaben}€`;
},
abheben(betrag) {
if (betrag > guthaben) throw new Error("Nicht genug Guthaben");
guthaben -= betrag;
return `Abgehoben: ${betrag}€. Neues Guthaben: ${guthaben}€`;
},
guthabenAnzeigen() {
return `Aktuelles Guthaben: ${guthaben}€`;
}
};
}
const konto = BankKonto(1000);
console.log(konto.einzahlen(500)); // "Eingezahlt: 500€. Neues Guthaben: 1500€"
console.log(konto.abheben(200)); // "Abgehoben: 200€. Neues Guthaben: 1300€"
// console.log(konto.guthaben); // undefined – nicht direkt zugänglich
// Klassischer Closure-Bug und die Lösung
// Bug (var):
for (var i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i); // Gibt dreimal "3" aus, nicht 0, 1, 2
}, 1000);
}
// Lösung 1: let verwenden (block-scoped)
for (let j = 0; j < 3; j++) {
setTimeout(function() {
console.log(j); // 0, 1, 2 ✓
}, 1000);
}
// Lösung 2: IIFE (Immediately Invoked Function Expression)
for (var k = 0; k < 3; k++) {
(function(index) {
setTimeout(function() {
console.log(index); // 0, 1, 2 ✓
}, 1000);
})(k);
}
Frage 3: Was ist Event Delegation?
Antwort:
Event Delegation nutzt Event Bubbling, um einen einzigen Event-Listener auf einem Elternelement zu platzieren, anstatt auf jedem Kindelement einen eigenen Listener zu registrieren.
// Ohne Event Delegation (schlecht bei dynamischen Listen)
const buttons = document.querySelectorAll('.action-button');
buttons.forEach(button => {
button.addEventListener('click', function(e) {
console.log('Button geklickt:', this.textContent);
});
// Problem: Dynamisch hinzugefügte Buttons werden nicht erfasst
});
// Mit Event Delegation (besser)
const container = document.getElementById('button-container');
container.addEventListener('click', function(e) {
// Prüfen, ob das angeklickte Element ein Button ist
if (e.target.matches('.action-button')) {
console.log('Button geklickt:', e.target.textContent);
e.target.classList.toggle('aktiv');
}
// Für verschachtelte Elemente: closest() verwenden
const listItem = e.target.closest('.list-item');
if (listItem) {
const itemId = listItem.dataset.id;
handleListItemClick(itemId);
}
});
// Vorteile:
// 1. Nur ein Event-Listener statt n Event-Listener
// 2. Funktioniert automatisch mit dynamisch hinzugefügten Elementen
// 3. Weniger Speicherverbrauch
Frage 4: Erkläre das Prototype-System in JavaScript
Antwort:
// Prototyp-Kette verstehen
function Tier(name, art) {
this.name = name;
this.art = art;
}
// Methode auf dem Prototyp (geteilt von allen Instanzen)
Tier.prototype.vorstellen = function() {
return `Ich bin ${this.name}, ein ${this.art}`;
};
Tier.prototype.lautMachen = function() {
return "...";
};
function Hund(name) {
Tier.call(this, name, "Hund"); // Elternkonstruktor aufrufen
this.tricks = [];
}
// Prototyp-Kette aufbauen
Hund.prototype = Object.create(Tier.prototype);
Hund.prototype.constructor = Hund;
// Methode überschreiben (Polymorphismus)
Hund.prototype.lautMachen = function() {
return "Wuff!";
};
Hund.prototype.trickLernen = function(trick) {
this.tricks.push(trick);
return `${this.name} hat "${trick}" gelernt!`;
};
const bello = new Hund("Bello");
console.log(bello.vorstellen()); // "Ich bin Bello, ein Hund"
console.log(bello.lautMachen()); // "Wuff!"
console.log(bello.trickLernen("Sitz")); // "Bello hat "Sitz" gelernt!"
// Prototyp-Kette prüfen
console.log(bello instanceof Hund); // true
console.log(bello instanceof Tier); // true
console.log(Object.getPrototypeOf(bello) === Hund.prototype); // true
// Modernes Äquivalent mit Class-Syntax (syntaktischer Zucker)
class ModernesTier {
constructor(name, art) {
this.name = name;
this.art = art;
}
vorstellen() {
return `Ich bin ${this.name}, ein ${this.art}`;
}
lautMachen() {
return "...";
}
}
class ModernerHund extends ModernesTier {
#tricks = []; // Private Felder (ES2022)
constructor(name) {
super(name, "Hund");
}
lautMachen() {
return "Wuff!";
}
trickLernen(trick) {
this.#tricks.push(trick);
return `${this.name} hat "${trick}" gelernt!`;
}
get anzahlTricks() {
return this.#tricks.length;
}
}
Asynchrone Programmierung
Frage 5: Erkläre Promise, async/await und den Event Loop
Antwort:
// Der Event Loop: Grundlegendes Verständnis
console.log("1 – Start (synchron)");
setTimeout(() => {
console.log("4 – setTimeout Callback (Macro Task)");
}, 0);
Promise.resolve().then(() => {
console.log("3 – Promise Callback (Micro Task)");
});
console.log("2 – Ende (synchron)");
// Ausgabe: 1, 2, 3, 4
// Wichtig: Micro Tasks (Promises) haben Vorrang vor Macro Tasks (setTimeout)
// Promises: Die Basis
function datenabrufen(id) {
return new Promise((resolve, reject) => {
// Simulierter API-Aufruf
setTimeout(() => {
if (id > 0) {
resolve({ id, name: `Benutzer ${id}`, email: `user${id}@example.de` });
} else {
reject(new Error("Ungültige ID"));
}
}, 500);
});
}
// Promise-Chaining
datenabrufen(1)
.then(user => {
console.log("Benutzer:", user);
return datenabrufen(2); // Gibt neues Promise zurück
})
.then(user2 => {
console.log("Benutzer 2:", user2);
})
.catch(fehler => {
console.error("Fehler:", fehler.message);
})
.finally(() => {
console.log("Fertig – wird immer ausgeführt");
});
// async/await: Sauberere Syntax
async function benutzerdatenLaden(userId) {
try {
const benutzer = await datenabrufen(userId);
console.log("Geladen:", benutzer);
return benutzer;
} catch (fehler) {
console.error("Ladefehler:", fehler.message);
throw fehler; // Fehler weiterwerfen
}
}
// Parallele Ausführung mit Promise.all
async function alleBenutzerdatenLaden() {
try {
// Alle Anfragen werden parallel gestartet (nicht sequenziell!)
const [user1, user2, user3] = await Promise.all([
datenabrufen(1),
datenabrufen(2),
datenabrufen(3)
]);
return [user1, user2, user3];
} catch (fehler) {
// Schlägt fehl, sobald EINE Anfrage scheitert
console.error("Eine Anfrage ist fehlgeschlagen:", fehler.message);
}
}
// Promise.allSettled: Wartet auf alle, unabhängig vom Ergebnis
async function alleErgebnisseSammeln() {
const ergebnisse = await Promise.allSettled([
datenabrufen(1),
datenabrufen(-1), // Wird fehlschlagen
datenabrufen(3)
]);
ergebnisse.forEach(ergebnis => {
if (ergebnis.status === "fulfilled") {
console.log("Erfolg:", ergebnis.value);
} else {
console.error("Fehlgeschlagen:", ergebnis.reason.message);
}
});
}
Für das Testen von regulären Ausdrücken in deinen asynchronen Validierungsfunktionen: Regex Tester.
Frage 6: Was sind Generator-Funktionen und wofür werden sie verwendet?
Antwort:
// Generator-Funktion: gibt einen Iterator zurück
function* zahlenGenerator() {
yield 1;
yield 2;
yield 3;
return 4; // done: true
}
const gen = zahlenGenerator();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: 3, done: false }
console.log(gen.next()); // { value: 4, done: true }
console.log(gen.next()); // { value: undefined, done: true }
// Praktisches Beispiel: Unendliche Sequenz
function* fibonacciSequenz() {
let [a, b] = [0, 1];
while (true) {
yield a;
[a, b] = [b, a + b];
}
}
const fib = fibonacciSequenz();
for (let i = 0; i < 10; i++) {
process.stdout.write(fib.next().value + " ");
}
// 0 1 1 2 3 5 8 13 21 34
// Generator mit async-ähnlicher Logik (Coroutines)
function* aufgabenManager() {
const benutzerId = yield "Bitte Benutzer-ID eingeben";
const benutzer = yield `API-Aufruf für Benutzer ${benutzerId}`;
const ergebnis = yield `Verarbeite Benutzer: ${benutzer}`;
return `Fertig: ${ergebnis}`;
}
Fortgeschrittene Themen
Frage 7: Erkläre das this-Schlüsselwort in verschiedenen Kontexten
// 1. Globaler Kontext
console.log(this); // window (Browser) oder {} (Node.js Modul)
// 2. Objekt-Methode
const objekt = {
name: "MeinObjekt",
zeigeName() {
return this.name; // 'this' verweist auf 'objekt'
},
zeigeNamePfeil: () => {
return this.name; // 'this' verweist auf den äußeren Scope (NICHT objekt)!
}
};
console.log(objekt.zeigeName()); // "MeinObjekt"
console.log(objekt.zeigeNamePfeil()); // undefined (häufiger Fehler!)
// 3. Konstruktorfunktion
function Person(name) {
this.name = name;
this.vorstellen = function() {
return `Hallo, ich bin ${this.name}`;
};
}
const alice = new Person("Alice");
console.log(alice.vorstellen()); // "Hallo, ich bin Alice"
// 4. Event-Handler
class Button {
constructor(label) {
this.label = label;
this.klickZaehler = 0;
}
// Pfeilfunktion: 'this' ist lexikalisch gebunden
handleKlickPfeil = () => {
this.klickZaehler++;
return `${this.label} wurde ${this.klickZaehler}x geklickt`;
};
// Reguläre Methode: 'this' muss gebunden werden
handleKlickRegular() {
this.klickZaehler++;
return `${this.label} wurde ${this.klickZaehler}x geklickt`;
}
}
const btn = new Button("Senden");
// Problem mit regulärer Methode als Callback
const handler = btn.handleKlickRegular;
// handler(); // TypeError: Cannot read property 'klickZaehler' of undefined
// Lösung: bind()
const gebundenerHandler = btn.handleKlickRegular.bind(btn);
console.log(gebundenerHandler()); // Funktioniert
// 5. call(), apply(), bind() im Vergleich
function begruessung(grussformel, interpunktion) {
return `${grussformel}, ${this.name}${interpunktion}`;
}
const person1 = { name: "Hans" };
const person2 = { name: "Maria" };
// call: Argumente einzeln übergeben
console.log(begruessung.call(person1, "Hallo", "!")); // "Hallo, Hans!"
// apply: Argumente als Array
console.log(begruessung.apply(person2, ["Guten Morgen", "."])); // "Guten Morgen, Maria."
// bind: Neue Funktion mit gebundenem 'this'
const hans_gruss = begruessung.bind(person1);
console.log(hans_gruss("Tschüss", "...")); // "Tschüss, Hans..."
Frage 8: Was ist das Module-System in JavaScript?
Antwort:
// ES Modules (moderner Standard)
// math.js – Exportieren
export const PI = 3.14159265;
export function addieren(a, b) {
return a + b;
}
export function multiplizieren(a, b) {
return a * b;
}
// Default Export: Nur einen pro Modul
export default class Rechner {
constructor() {
this.verlauf = [];
}
berechnen(operation, a, b) {
let ergebnis;
switch (operation) {
case '+': ergebnis = addieren(a, b); break;
case '*': ergebnis = multiplizieren(a, b); break;
default: throw new Error(`Unbekannte Operation: ${operation}`);
}
this.verlauf.push({ operation, a, b, ergebnis });
return ergebnis;
}
}
// main.js – Importieren
import Rechner, { PI, addieren } from './math.js'; // Default + Named Import
import * as MathUtils from './math.js'; // Namespace Import
import { multiplizieren as mult } from './math.js'; // Alias
const rechner = new Rechner();
console.log(rechner.berechnen('+', 5, 3)); // 8
console.log(PI); // 3.14159265
console.log(addieren(2, 3)); // 5
console.log(mult(4, 5)); // 20
// Dynamische Imports (für Code-Splitting)
async function schwereLadeoperationen() {
if (process.env.NODE_ENV === 'development') {
// Wird nur bei Bedarf geladen
const { debugHelper } = await import('./debug-utils.js');
debugHelper.aktivieren();
}
}
Frage 9: Performance-Optimierung in JavaScript
Antwort:
// 1. Debounce: Verzögert die Ausführung bis nach der letzten Eingabe
function debounce(fn, wartezeit) {
let timeout;
return function(...args) {
clearTimeout(timeout);
timeout = setTimeout(() => {
fn.apply(this, args);
}, wartezeit);
};
}
const sucheOptimiert = debounce((suchbegriff) => {
console.log(`Suche nach: ${suchbegriff}`);
// API-Aufruf hier
}, 300);
// Wird 300ms nach der letzten Eingabe aufgerufen
document.getElementById('suche')?.addEventListener('input', (e) => {
sucheOptimiert(e.target.value);
});
// 2. Throttle: Begrenzt die Ausführungsrate
function throttle(fn, limit) {
let imKuehlmodus = false;
return function(...args) {
if (!imKuehlmodus) {
fn.apply(this, args);
imKuehlmodus = true;
setTimeout(() => { imKuehlmodus = false; }, limit);
}
};
}
const scrollHandler = throttle(() => {
console.log('Scroll-Position:', window.scrollY);
}, 100); // Maximal alle 100ms ausführen
window.addEventListener('scroll', scrollHandler);
// 3. Memoization: Ergebnisse cachen
function memoize(fn) {
const cache = new Map();
return function(...args) {
const schluessel = JSON.stringify(args);
if (cache.has(schluessel)) {
console.log('Aus Cache geladen!');
return cache.get(schluessel);
}
const ergebnis = fn.apply(this, args);
cache.set(schluessel, ergebnis);
return ergebnis;
};
}
// Fibonacci mit Memoization
const fibMemo = memoize(function fib(n) {
if (n <= 1) return n;
return fibMemo(n - 1) + fibMemo(n - 2);
});
console.log(fibMemo(40)); // Schnell!
console.log(fibMemo(40)); // Aus Cache!
// 4. Web Workers für CPU-intensive Aufgaben
// main.js
if (typeof Worker !== 'undefined') {
const worker = new Worker('berechnung.worker.js');
worker.postMessage({ aufgabe: 'primzahlen', bis: 1000000 });
worker.onmessage = function(e) {
console.log('Primzahlen berechnet:', e.data.anzahl);
};
worker.onerror = function(fehler) {
console.error('Worker-Fehler:', fehler.message);
};
}
// berechnung.worker.js
self.onmessage = function(e) {
const { aufgabe, bis } = e.data;
if (aufgabe === 'primzahlen') {
const primzahlen = [];
for (let i = 2; i <= bis; i++) {
if (istPrimzahl(i)) primzahlen.push(i);
}
self.postMessage({ anzahl: primzahlen.length });
}
};
function istPrimzahl(n) {
for (let i = 2; i <= Math.sqrt(n); i++) {
if (n % i === 0) return false;
}
return n > 1;
}
Frage 10: Erkläre WeakMap, WeakSet und wann du sie verwendest
Antwort:
// Map vs WeakMap
// Map: Starke Referenz, verhindert Garbage Collection
const map = new Map();
let schluessel = { id: 1 };
map.set(schluessel, "Daten");
schluessel = null; // Objekt wird NICHT vom GC gesammelt (Map hält Referenz)
// WeakMap: Schwache Referenz, erlaubt Garbage Collection
const weakMap = new WeakMap();
let schwacherSchluessel = { id: 2 };
weakMap.set(schwacherSchluessel, "WeakMap-Daten");
schwacherSchluessel = null; // Objekt KANN nun vom GC gesammelt werden
// Praktische Verwendung: Private Daten für Klassen
const _privateData = new WeakMap();
class SecureContainer {
constructor(initialData) {
_privateData.set(this, {
daten: initialData,
erstellt: new Date()
});
}
getDaten() {
return _privateData.get(this).daten;
}
setDaten(neueDaten) {
const existing = _privateData.get(this);
_privateData.set(this, {
...existing,
daten: neueDaten,
geaendert: new Date()
});
}
}
const container = new SecureContainer("geheime Daten");
console.log(container.getDaten()); // "geheime Daten"
// Kein direkter Zugriff auf private Daten möglich
// WeakSet: Set mit schwachen Referenzen
const verarbeiteteObjekte = new WeakSet();
function verarbeite(objekt) {
if (verarbeiteteObjekte.has(objekt)) {
return "Bereits verarbeitet";
}
// Verarbeitung...
verarbeiteteObjekte.add(objekt);
return "Verarbeitet";
}
Typische Coding-Aufgaben im Interview
Aufgabe: Implementiere Array.prototype.flat von Grund auf
// Tiefes Flatten eines Arrays
Array.prototype.meinFlat = function(tiefe = 1) {
if (tiefe === 0) return this.slice();
return this.reduce((acc, element) => {
if (Array.isArray(element) && tiefe > 0) {
acc.push(...element.meinFlat(tiefe - 1));
} else {
acc.push(element);
}
return acc;
}, []);
};
console.log([1, [2, [3, [4]]]].meinFlat()); // [1, 2, [3, [4]]]
console.log([1, [2, [3, [4]]]].meinFlat(2)); // [1, 2, 3, [4]]
console.log([1, [2, [3, [4]]]].meinFlat(Infinity)); // [1, 2, 3, 4]
Aufgabe: Implementiere eine Curry-Funktion
function curry(fn) {
return function gekurvt(...args) {
if (args.length >= fn.length) {
// Genug Argumente: Funktion aufrufen
return fn.apply(this, args);
} else {
// Noch nicht genug: Neue Funktion zurückgeben
return function(...weitereArgs) {
return gekurvt.apply(this, args.concat(weitereArgs));
};
}
};
}
function addieren3(a, b, c) {
return a + b + c;
}
const curriedAddieren = curry(addieren3);
console.log(curriedAddieren(1)(2)(3)); // 6
console.log(curriedAddieren(1, 2)(3)); // 6
console.log(curriedAddieren(1)(2, 3)); // 6
console.log(curriedAddieren(1, 2, 3)); // 6
// Praktisch: Wiederverwendbare Teilfunktionen
const addiere10 = curriedAddieren(10);
const addiere10und20 = addiere10(20);
console.log(addiere10und20(5)); // 35
console.log(addiere10und20(15)); // 45
Tipps für dein nächstes JavaScript-Interview
Vorbereitung
- Übe täglich mit Coding-Challenges auf Plattformen wie LeetCode (auf Deutsch stellen, wenn vorhanden), Codewars oder HackerRank
- Verstehe, nicht memoriere – Interviewer merken den Unterschied sofort
- Denke laut – erkläre deinen Denkprozess, auch wenn du dir nicht sicher bist
- Frage nach Edge Cases – zeigt strukturiertes Denken
- Schreibe lesbaren Code – Variablennamen, Kommentare, Struktur
Häufige Fehler vermeiden
- Nicht sagen "Das kenne ich nicht" – sage stattdessen "Ich würde das Problem so angehen..."
- Nicht sofort mit dem Coden anfangen – erst die Anforderungen vollständig verstehen
- Edge Cases vergessen (leere Arrays, null, undefined, 0, negative Zahlen)
- Keine Tests schreiben – zeige proaktiv, wie du deinen Code testen würdest
Ressourcen
Für das Testen von Regex-Mustern in JavaScript-Validierungsfunktionen: Regex Tester. Zum Experimentieren mit JSON-Datenstrukturen: JSON Formatter.
Fazit
JavaScript-Interviews sind 2026 anspruchsvoller denn je, aber mit systematischer Vorbereitung und echtem Verständnis der Kernkonzepte bist du gut aufgestellt. Fokussiere dich auf Closures, asynchrone Programmierung, das this-Keyword und Performance-Optimierung – diese Themen tauchen in fast jedem Interview auf.
Denk daran: Interviewer suchen nicht nach perfekter Syntax, sondern nach Entwicklern, die Probleme strukturiert lösen können und bereit sind, zu lernen und zu wachsen.
Hilfreiche Tools für JavaScript-Entwickler: JSON Formatter | Regex Tester | Hash Generator | JWT Decoder