{
  "assessmentTests": {
    "javascript_test": {
      "name": "JavaScript-Test",
      "desc": "30 Szenario-Fragen zu Kernsyntax und Typenzwang, Funktionen/Geltungsbereich/Abschlüsse und this, Objekte/Arrays und Datenstrukturen, und Event Loop/Async/Prototype-Idiome — überprüfen Sie, ob Ihre JavaScript-Fähigkeiten dem entsprechen, was Stellenausschreibungen unter JavaScript-Kompetenz verstehen.",
      "recommendation": "Ihre JavaScript-Fähigkeitsprofil",
      "results": {
        "beginner": {
          "name": "Anfänger",
          "desc": "Sie können funktionierende Funktionen und Objekte schreiben, aber die Fragen, die Sie verfehlt haben, konzentrieren sich auf Typenzwang und Referenzsemantik statt Syntax — == wandelt Typen vor dem Vergleich stillschweigend um, eine Array-Zuweisung, die ein Objekt gemeinsam nutzt statt es zu kopieren, warum NaN sich selbst nie gleichkommt. Das liegt nicht daran, dass Sie schlecht in JavaScript sind; dies sind die spezifischen Regeln, die Menschen verwirren, die die Sprache gelernt haben, indem sie Code schrieben, der zufällig funktionierte, statt zu lernen, wie Werte und Referenzen sich wirklich verhalten. Sie sind bei der Arbeit wichtig, weil jede einzelne der Ort ist, an dem Code ohne Fehler ausgeführt wird und trotzdem das Falsche tut.",
          "recommendation": "Beginnen Sie mit drei Dingen, in dieser Reihenfolge: warum == Typen vor dem Vergleich umwandelt, während === nicht umwandelt, warum die Zuweisung eines Arrays oder Objekts an eine neue Variable die Referenz kopiert, nicht den Wert, und wie var hochgezogen und zu undefined initialisiert wird, während let und const in der zeitlichen Totzone bleiben, bis ihre Deklaration ausgeführt wird. Das MDN JavaScript-Leitfaden behandelt alle drei mit ausführbaren Beispielen."
        },
        "intermediate": {
          "name": "Mittelstufe",
          "desc": "Sie handhaben alltäglichen Anwendungscode bequem — Funktionen, Array-Methoden, unkomplizierten Kontrollfluss — und werden nicht durch Routine-Wartungsarbeiten verlangsamt. Die Lücke zwischen hier und Fortgeschritten befasst sich hauptsächlich damit, was passiert, wenn JavaScripts Geltungsbereichs- und Timing-Regeln interagieren: ein var, das in Callback-Aufrufen einer Schleife erfasst wird und alle denselben endgültigen Wert lesen, ein this, das stillschweigend nicht mehr auf das Objekt zeigt, sobald die Methode davon getrennt wird, eine Spread-Kopie, die tief aussieht, aber nur eine Ebene kopiert. Dies sind die Arten von Fehlern, die bei einer schnellen Überprüfung bestehen und nur unter einer spezifischen Laufzeitbedingung auftauchen.",
          "recommendation": "Konzentrieren Sie sich darauf, wie Geltungsbereich und this zur Aufrufzeit, nicht zur Definitionszeit aufgelöst werden: warum eine var-basierte for-Schleife jedem Callback denselben endgültigen Zählerwert übergibt, während let das nicht tut, wie this davon bestimmt wird, wie eine Funktion aufgerufen wird, nicht wo sie geschrieben ist, und warum eine Spread- oder Object.assign-Kopie nur die oberste Ebene eines Objekts kopiert. Dann die Event Loop, weil das die Leute verwirrt, die Promises bereits einzeln kennen."
        },
        "advanced": {
          "name": "Fortgeschritten",
          "desc": "Dies ist die Ebene, die die meisten Stellenausschreibungen unter \"starkem JavaScript\" verstehen. Sie lesen eine Kette von filter/map/reduce und können genau vorhersagen, was sie zurückgibt, ohne das ursprüngliche Array zu ändern, Sie wissen, warum ein synchrones console.log immer eine Microtask schlägt, die einen setTimeout schlägt, und Sie greifen zu Promise.all wissend, dass es sofort ablehnt, wenn ein Promise ablehnt, statt auf den Rest zu warten. Das, was diese Stufe von der höchsten trennt, ist die Prototype- und Objektmodell-Seite der Arbeit: wie die Prototype-Kette eine geerbte Methode gegen hasOwnProperty auflöst, und was ein privates Klassenfeld wirklich verbirgt.",
          "recommendation": "Drücken Sie in die Teile, die den Status schützen, den anderer Code auch berührt: wie JavaScripts Prototype-Kette Methodennachschlag für dinge auflöst, die nicht auf der Instanz selbst sind, was private #fields einer Klasse wirklich vor externem Zugriff verstecken, und wie JSON.stringify undefined und Funktionen anders behandelt, wenn sie in einem einfachen Objekt gegenüber in einem Array sind. MDNs Material zur Event Loop und Prototype-Kette ist die natürliche nächste Station für beide."
        },
        "expert": {
          "name": "Experte",
          "desc": "Sie haben in jedem Abschnitt oben gescort — Kernsyntax, Typen und Typenzwang; Funktionen, Geltungsbereich, Abschlüsse und this; Objekte, Arrays und Datenstrukturen; und Async, Event Loop, Prototypes und Idiome. Praktisch bedeutet das, dass Sie eine fremde Funktion übergeben bekommen und erklären können, warum sie das zurückgibt, nicht nur was die Syntax sagen sollte, was die schwierigere und wertvollere Fähigkeit ist. Auf dieser Stufe ist die Sprache selbst selten der begrenzende Faktor; das Limit ist normalerweise asynchroner Kontrollfluss oder die Form der Daten darunter.",
          "recommendation": "Die Rückgaben sind jetzt in Design und Diagnose: Überlegungen zu einer Race Condition zwischen Promise.all und Promise.race statt Nachsprechen ihrer Definitionen, Wahl, wann ein Abschluss das richtige Werkzeug für privaten Status ist versus wann es ein privates Feld einer Klasse ist, und Lesung eines Microtask-vs-Macrotask-Ordering-Fehlers in Produktionsprotokollen statt in einem Quiz. Wenn Sie für eine Rolle überprüft werden, beschreiben Sie einen Timing-Fehler, den Sie gefunden haben — eine veraltete Closure-Variable oder einen asynchronen Callback außer Reihenfolge — anstatt JavaScript-Funktionen zu benennen; es zeigt das Denken, nicht nur das Vokabular."
        }
      },
      "questions": [
        {
          "question": "console.log(x);\nvar x = 5;\nWas wird dies ausgedruckt?",
          "options": [
            {
              "icon": "",
              "label": "5 — var-Deklarationen werden sofort ausgeführt, vor allen anderen Code"
            },
            {
              "icon": "",
              "label": "ReferenceError: x is not defined"
            },
            {
              "icon": "",
              "label": "undefined — var-Deklarationen werden an die Oberseite ihres Geltungsbereichs hochgezogen und zu undefined initialisiert, aber die Zuweisung wird an Ort und Stelle ausgeführt, also besteht die Variable vor der Ausführung, hält aber undefined"
            },
            {
              "icon": "",
              "label": "null"
            }
          ]
        },
        {
          "question": "console.log(y);\nlet y = 5;\nWas passiert?",
          "options": [
            {
              "icon": "",
              "label": "Es wirft ReferenceError: Cannot access 'y' before initialization — let wird an die Oberseite ihres Geltungsbereichs hochgezogen, aber bleibt in der zeitlichen Totzone, bis die Deklarationszeile tatsächlich ausgeführt wird"
            },
            {
              "icon": "",
              "label": "Es wird undefined gedruckt, dasselbe wie var"
            },
            {
              "icon": "",
              "label": "Es wird 5 gedruckt"
            },
            {
              "icon": "",
              "label": "Es wirft einen SyntaxError zur Analyse-Zeit"
            }
          ]
        },
        {
          "question": "const arr = [1, 2, 3];\narr.push(4);\nconsole.log(arr.length);\nWas wird dies ausgedruckt?",
          "options": [
            {
              "icon": "",
              "label": "4 — const verhindert nur das Neuzuweisen der arr-Bindung selbst, nicht das Ändern des Array-Objekts, auf das es zeigt, daher push funktioniert trotzdem"
            },
            {
              "icon": "",
              "label": "Es wirft einen TypeError, weil arr const ist"
            },
            {
              "icon": "",
              "label": "3 — push ist auf einem const-Array blockiert"
            },
            {
              "icon": "",
              "label": "undefined"
            }
          ]
        },
        {
          "question": "console.log('' == 0);\nconsole.log('' === 0);\nWas geben die beiden Zeilen aus?",
          "options": [
            {
              "icon": "",
              "label": "true dann true"
            },
            {
              "icon": "",
              "label": "false dann false"
            },
            {
              "icon": "",
              "label": "true dann false — == zwingt den leeren String zu 0 vor dem Vergleich, während === Typ und Wert ohne Zwang vergleicht, und ein String ist nie streng gleich einer Zahl"
            },
            {
              "icon": "",
              "label": "false dann true"
            }
          ]
        },
        {
          "question": "console.log(NaN === NaN);\nconsole.log(Number.isNaN(NaN));\nWas geben die beiden Zeilen aus?",
          "options": [
            {
              "icon": "",
              "label": "true dann true"
            },
            {
              "icon": "",
              "label": "true dann false"
            },
            {
              "icon": "",
              "label": "false dann false"
            },
            {
              "icon": "",
              "label": "false dann true — NaN ist der einzige Wert in JavaScript, der sich selbst unter === nicht gleicht, aber Number.isNaN() erkennt es trotzdem korrekt"
            }
          ]
        },
        {
          "question": "console.log(typeof null);\nWas wird dies ausgedruckt?",
          "options": [
            {
              "icon": "",
              "label": "\"null\""
            },
            {
              "icon": "",
              "label": "\"object\" — ein lange bestehender Fehler in der Sprache: typeof null gibt \"object\" zurück, obwohl null primitiv ist, und es wurde beibehalten, um Rückwärtskompatibilität seit jeher zu wahren"
            },
            {
              "icon": "",
              "label": "\"undefined\""
            },
            {
              "icon": "",
              "label": "\"boolean\""
            }
          ]
        },
        {
          "question": "Welches der folgenden ist truthy bei Verwendung in einer if-Bedingung?",
          "options": [
            {
              "icon": "",
              "label": "0"
            },
            {
              "icon": "",
              "label": "\"\" (ein leerer String)"
            },
            {
              "icon": "",
              "label": "null"
            },
            {
              "icon": "",
              "label": "\"0\" — ein nicht leerer String ist immer truthy in JavaScript, selbst wenn sein Inhalt der String \"0\" selbst ist"
            }
          ]
        },
        {
          "question": "foo();\nfunction foo() { console.log('a'); }\nbar();\nvar bar = function() { console.log('b'); };\nWas passiert?",
          "options": [
            {
              "icon": "",
              "label": "Beide Aufrufe werden gedruckt, \"a\" dann \"b\""
            },
            {
              "icon": "",
              "label": "foo() druckt \"a\"; bar() wirft TypeError: bar is not a function — Funktionsdeklarationen werden vollständig mit ihrem Körper hochgezogen, aber eine var-zugewiesene Funktionsexpression hebt nur das var selbst (als undefined) auf, nicht die Zuweisung"
            },
            {
              "icon": "",
              "label": "Beide Aufrufe werfen ReferenceError"
            },
            {
              "icon": "",
              "label": "foo() wirft; bar() druckt \"b\""
            }
          ]
        },
        {
          "question": "function makeCounter() {\n  let count = 0;\n  return function() { count++; return count; };\n}\nconst counter = makeCounter();\nconsole.log(counter());\nconsole.log(counter());\nWas geben die beiden Zeilen aus?",
          "options": [
            {
              "icon": "",
              "label": "1 dann 1"
            },
            {
              "icon": "",
              "label": "0 dann 1"
            },
            {
              "icon": "",
              "label": "undefined dann undefined"
            },
            {
              "icon": "",
              "label": "1 dann 2 — die zurückgegebene Funktion schließt über die gleiche count-Variable aus dem makeCounter-Geltungsbereich, und diese Variable besteht zwischen Aufrufen, anstatt zurückgesetzt zu werden"
            }
          ]
        },
        {
          "question": "for (var i = 0; i < 3; i++) {\n  setTimeout(() => console.log(i), 0);\n}\nWas wird dies ausgedruckt?",
          "options": [
            {
              "icon": "",
              "label": "0, 1, 2"
            },
            {
              "icon": "",
              "label": "3, 3, 3 — var ist funktionsgescoped, nicht blockgescoped, daher schließen alle drei Callbacks über das gleiche i, das die Schleife bereits beendet hat und gleich 3 ist, wenn einer von ihnen tatsächlich ausgeführt wird"
            },
            {
              "icon": "",
              "label": "0, 0, 0"
            },
            {
              "icon": "",
              "label": "undefined, undefined, undefined"
            }
          ]
        },
        {
          "question": "for (let i = 0; i < 3; i++) {\n  setTimeout(() => console.log(i), 0);\n}\nWas wird dies ausgedruckt?",
          "options": [
            {
              "icon": "",
              "label": "3, 3, 3"
            },
            {
              "icon": "",
              "label": "0, 0, 0"
            },
            {
              "icon": "",
              "label": "undefined, undefined, undefined"
            },
            {
              "icon": "",
              "label": "0, 1, 2 — let erstellt eine neue Bindung von i für jede Schleifeniteration, daher schließt jeder Callback über seine eigene separate Kopie, nicht über eine gemeinsame Variable"
            }
          ]
        },
        {
          "question": "function greet() { return `Hi, ${this.name}`; }\nconst person = { name: 'Ana' };\nconsole.log(greet.call(person));\nWas wird dies ausgedruckt?",
          "options": [
            {
              "icon": "",
              "label": "\"Hi, Ana\" — Function.prototype.call ruft die Funktion auf, wobei this auf sein erstes Argument gesetzt ist, daher wird this.name gegen person aufgelöst"
            },
            {
              "icon": "",
              "label": "\"Hi, undefined\" — call() funktioniert nur bei Funktionen, die bereits Methoden eines Objekts sind"
            },
            {
              "icon": "",
              "label": "Es wirft einen TypeError, weil greet keine Methode von person ist"
            },
            {
              "icon": "",
              "label": "\"Hi, ${this.name}\" — call() wertet das Template-Literal nicht aus"
            }
          ]
        },
        {
          "question": "const timer = {\n  seconds: 0,\n  start: function() {\n    setInterval(() => { this.seconds++; }, 1000);\n  },\n};\nWarum bevorzugen Menschen die Pfeilfunktion gegenüber function() { this.seconds++ } innen setInterval hier?",
          "options": [
            {
              "icon": "",
              "label": "Pfeilfunktionen haben ihr eigenes this nicht — sie erfassen this lexikalisch aus der umschließenden start-Funktion, die timer ist (da start als timer.start() aufgerufen wurde), daher verweist this.seconds korrekt auf timer.seconds bei jedem Tick"
            },
            {
              "icon": "",
              "label": "Pfeilfunktionen führen zur Laufzeit schneller aus als reguläre Funktionen"
            },
            {
              "icon": "",
              "label": "Pfeilfunktionen binden this automatisch an das globale Objekt, das zufällig timer entspricht"
            },
            {
              "icon": "",
              "label": "Pfeilfunktionen konvertieren this automatisch in eine globale Variable"
            }
          ]
        },
        {
          "question": "function multiply(a, b) { return a * b; }\nconst double = multiply.bind(null, 2);\nconsole.log(double(5));\nWas wird dies ausgedruckt?",
          "options": [
            {
              "icon": "",
              "label": "Es wirft einen Fehler, weil bind beide Argumente im Voraus erfordert"
            },
            {
              "icon": "",
              "label": "10 — bind gibt eine neue Funktion mit this und führenden Argumenten voreingestellt zurück; double(5) aufrufen liefert das verbleibende Argument b=5, daher wird multiply(2, 5) ausgeführt"
            },
            {
              "icon": "",
              "label": "NaN"
            },
            {
              "icon": "",
              "label": "7, weil bind seine Argumente zum Aufruf addiert, anstatt sie zu ersetzen"
            }
          ]
        },
        {
          "question": "function outer() {\n  let x = 10;\n  function inner() {\n    let x = 20;\n    console.log(x);\n  }\n  inner();\n  console.log(x);\n}\nouter();\nWas geben die beiden console.log-Aufrufe aus?",
          "options": [
            {
              "icon": "",
              "label": "20 dann 20"
            },
            {
              "icon": "",
              "label": "20 dann 10 — inners let x shadowed outers x nur innerhalb von inners Geltungsbereich; outers x wird nie berührt"
            },
            {
              "icon": "",
              "label": "10 dann 10"
            },
            {
              "icon": "",
              "label": "Es wirft einen ReferenceError, weil x zweimal deklariert ist"
            }
          ]
        },
        {
          "question": "const a = [1, 2, 3];\nconst b = a;\nb.push(4);\nconsole.log(a.length);\nWas wird dies ausgedruckt?",
          "options": [
            {
              "icon": "",
              "label": "3 — b ist eine separate Kopie von a"
            },
            {
              "icon": "",
              "label": "undefined"
            },
            {
              "icon": "",
              "label": "Es wirft einen TypeError, weil a const ist"
            },
            {
              "icon": "",
              "label": "4 — Arrays sind Objekte, daher kopiert const b = a die Referenz, nicht das Array; das Ändern von b durch push ändert auch das gleiche Array, auf das a zeigt"
            }
          ]
        },
        {
          "question": "const original = { name: 'Ana', tags: ['x', 'y'] };\nconst copy = { ...original };\ncopy.name = 'Bea';\ncopy.tags.push('z');\nconsole.log(original.name, original.tags.length);\nWas wird dies ausgedruckt?",
          "options": [
            {
              "icon": "",
              "label": "'Ana' 2 — Spread klont tief jedes verschachtelte Objekt und Array"
            },
            {
              "icon": "",
              "label": "'Ana' 3 — Spread ist nur eine flache Kopie: eine primitive Eigenschaft wie name wird nach Wert kopiert, daher berührt das Neuzuweisen von copy.name original nie, aber ein verschachteltes Array wie tags wird nach Referenz kopiert, daher ändert das Drücken in copy.tags das gleiche Array, auf das original.tags zeigt"
            },
            {
              "icon": "",
              "label": "'Bea' 3 — Spread kopiert alles per Referenz, daher werden sowohl Top-Level- als auch verschachtelte Änderungen auf original zurücktransferiert"
            },
            {
              "icon": "",
              "label": "'Bea' 2"
            }
          ]
        },
        {
          "question": "const { a: x = 10, b: y = 20 } = { a: 5 };\nconsole.log(x, y);\nWas wird dies ausgedruckt?",
          "options": [
            {
              "icon": "",
              "label": "undefined undefined"
            },
            {
              "icon": "",
              "label": "5 undefined"
            },
            {
              "icon": "",
              "label": "10 20"
            },
            {
              "icon": "",
              "label": "5 20 — a wird zu x umbenannt und ist vorhanden, daher behält es seinen Wert 5; b wird zu y umbenannt und ist abwesend, daher fällt es auf seinen Standard 20 zurück"
            }
          ]
        },
        {
          "question": "const [first, ...rest] = [1, 2, 3, 4];\nconsole.log(rest);\nWas wird dies ausgedruckt?",
          "options": [
            {
              "icon": "",
              "label": "[1, 2, 3, 4]"
            },
            {
              "icon": "",
              "label": "[2, 3, 4] — rest sammelt jedes verbleibende Element nach first in ein völlig neues Array"
            },
            {
              "icon": "",
              "label": "2"
            },
            {
              "icon": "",
              "label": "[1]"
            }
          ]
        },
        {
          "question": "const nums = [10, 1, 2];\nnums.sort();\nconsole.log(nums);\nWas wird dies ausgedruckt?",
          "options": [
            {
              "icon": "",
              "label": "[1, 2, 10] — sort ordnet Zahlen standardmäßig immer numerisch in aufsteigender Reihenfolge"
            },
            {
              "icon": "",
              "label": "[10, 1, 2] — sort ändert das ursprüngliche Array nicht"
            },
            {
              "icon": "",
              "label": "[1, 10, 2] — Array.prototype.sorts Standardkomparator konvertiert Elemente in Strings und vergleicht sie lexikographisch, daher sortiert \"10\" vor \"2\"; sort ändert auch das Array vor Ort, anstatt ein neues zurückzugeben"
            },
            {
              "icon": "",
              "label": "[2, 1, 10]"
            }
          ]
        },
        {
          "question": "const user = { profile: { age: 0 } };\nconsole.log(user.profile?.age ?? 'unknown');\nconsole.log(user.address?.city ?? 'unknown');\nWas geben die beiden Zeilen aus?",
          "options": [
            {
              "icon": "",
              "label": "'unknown' dann 'unknown' — ?? behandelt 0 als fehlend, genauso wie || tut"
            },
            {
              "icon": "",
              "label": "0 dann 'unknown' — ?? fällt nur auf null oder undefined zurück, nicht auf andere falsy-Werte wie 0, daher wird age (0) beibehalten; user.address ist undefined, daher unterbricht ?. die ganze Kette zu undefined und ?? liefert dann 'unknown'"
            },
            {
              "icon": "",
              "label": "0 dann undefined"
            },
            {
              "icon": "",
              "label": "'unknown' dann 0"
            }
          ]
        },
        {
          "question": "const obj = { a: undefined, b: function(){}, c: NaN, d: [undefined, function(){}, 1] };\nconsole.log(JSON.stringify(obj));\nWas wird dies ausgedruckt?",
          "options": [
            {
              "icon": "",
              "label": "'{\"a\":null,\"b\":null,\"c\":null,\"d\":[null,null,1]}' — jeder nicht unterstützte Wert wird überall null, in Objekten und Arrays gleichermaßen"
            },
            {
              "icon": "",
              "label": "'{\"a\":undefined,\"b\":undefined,\"c\":NaN,\"d\":[undefined,null,1]}'"
            },
            {
              "icon": "",
              "label": "'{\"c\":null,\"d\":[null,null,1]}' — Objekteigenschaften, deren Wert undefined oder eine Funktion ist, werden ganz weggelassen, und NaN wird zu null serialisiert, aber innerhalb eines Arrays werden diese gleichen nicht unterstützten Werte statt gelöscht zu null, weil das Löschen eines Array-Elements jeden späteren Index verschieben würde"
            },
            {
              "icon": "",
              "label": "Es wirft einen TypeError, weil NaN nicht zu JSON serialisiert werden kann"
            }
          ]
        },
        {
          "question": "console.log('1');\nsetTimeout(() => console.log('2'), 0);\nPromise.resolve().then(() => console.log('3'));\nconsole.log('4');\nIn welcher Reihenfolge werden diese gedruckt?",
          "options": [
            {
              "icon": "",
              "label": "1, 2, 3, 4"
            },
            {
              "icon": "",
              "label": "1, 4, 2, 3"
            },
            {
              "icon": "",
              "label": "1, 4, 3, 2 — Synchroner Code wird zuerst ausgeführt (daher werden 1 und 4 sofort gedruckt), dann wird die Microtask-Warteschlange (Promise-Callbacks) vollständig geleert, bevor die Event Loop zum nächsten Macrotask übergeht, daher wird 3 vor setTimeout-Callback 2 gedruckt"
            },
            {
              "icon": "",
              "label": "1, 3, 4, 2"
            }
          ]
        },
        {
          "question": "async function foo() {\n  console.log('a');\n  await null;\n  console.log('b');\n}\nconsole.log('start');\nfoo();\nconsole.log('end');\nIn welcher Reihenfolge werden diese gedruckt?",
          "options": [
            {
              "icon": "",
              "label": "start, a, end, b — foo() wird synchron bis zu seinem ersten await ausgeführt, daher wird 'a' sofort protokolliert, wenn foo() aufgerufen wird; await unterbricht foo und gibt die Kontrolle dem Aufrufer zurück, daher wird 'end' protokolliert, bevor das fortgesetzte 'b' später als Microtask ausgeführt wird"
            },
            {
              "icon": "",
              "label": "start, a, b, end"
            },
            {
              "icon": "",
              "label": "start, end, a, b"
            },
            {
              "icon": "",
              "label": "a, start, end, b"
            }
          ]
        },
        {
          "question": "Promise.all([\n  Promise.resolve(1),\n  Promise.reject('err'),\n  Promise.resolve(3),\n]).then(r => console.log('ok', r)).catch(e => console.log('fail', e));\nWas wird dies ausgedruckt?",
          "options": [
            {
              "icon": "",
              "label": "'fail err' — Promise.all lehnt ab, sobald einer seiner Eingabe-Promises ablehnt, ignoriert die Ergebnisse der anderen, daher wird der catch-Handler mit diesem Ablehnungsgrund ausgeführt"
            },
            {
              "icon": "",
              "label": "'ok' [1, 3]"
            },
            {
              "icon": "",
              "label": "'ok' [1, undefined, 3]"
            },
            {
              "icon": "",
              "label": "Es wirft synchron und kein Handler wird je ausgeführt"
            }
          ]
        },
        {
          "question": "const fast = new Promise(res => setTimeout(() => res('fast'), 10));\nconst slow = new Promise(res => setTimeout(() => res('slow'), 100));\nPromise.race([fast, slow]).then(v => console.log(v));\nWas wird dies ausgedruckt?",
          "options": [
            {
              "icon": "",
              "label": "'slow'"
            },
            {
              "icon": "",
              "label": "['fast', 'slow']"
            },
            {
              "icon": "",
              "label": "'fast' — Promise.race siedelt an, sobald das erste seiner Eingabe-Promises sich siedelt, und der 10ms-Timer siedelt sich lange vor dem 100ms-Timer"
            },
            {
              "icon": "",
              "label": "Es wirft einen Fehler, weil race genau zwei Promises erfordert"
            }
          ]
        },
        {
          "question": "function Animal(name) { this.name = name; }\nAnimal.prototype.speak = function() { return `${this.name} makes a sound`; };\nconst dog = new Animal('Rex');\nconsole.log(dog.speak());\nconsole.log(dog.hasOwnProperty('speak'));\nWas geben die beiden Zeilen aus?",
          "options": [
            {
              "icon": "",
              "label": "'Rex makes a sound' dann true"
            },
            {
              "icon": "",
              "label": "undefined dann false"
            },
            {
              "icon": "",
              "label": "Es wirft einen TypeError, weil dog keine eigene speak-Methode hat"
            },
            {
              "icon": "",
              "label": "'Rex makes a sound' dann false — speak ist auf Animal.prototype definiert, nicht auf der dog-Instanz selbst; dog findet sie durch die Prototype-Kette, wenn sie aufgerufen wird, aber hasOwnProperty prüft nur die eigenen Eigenschaften der Instanz, nicht alles Geerbte"
            }
          ]
        },
        {
          "question": "class Counter {\n  #count = 0;\n  increment() { this.#count++; return this.#count; }\n}\nconst c = new Counter();\nconsole.log(c.increment());\nconsole.log(c.count);\nWas geben die beiden Zeilen aus?",
          "options": [
            {
              "icon": "",
              "label": "1 dann undefined — #count ist ein privates Klassenfeld, lesbar und beschreibbar nur von innen in den eigenen Methoden der Klasse; c.count (ohne #) ist eine völlig andere, gewöhnliche Eigenschaft, die niemals festgelegt wurde, daher ist sie undefined"
            },
            {
              "icon": "",
              "label": "1 dann 0"
            },
            {
              "icon": "",
              "label": "1 dann 1"
            },
            {
              "icon": "",
              "label": "Es wirft einen SyntaxError beim Zugriff auf c.count"
            }
          ]
        },
        {
          "question": "const items = ['a', 'b'];\nconst label = `Items: ${items.length > 0 ? items.join(', ') : 'none'}`;\nconsole.log(label);\nWas wird dies ausgedruckt?",
          "options": [
            {
              "icon": "",
              "label": "'Items: ${items.length > 0 ? items.join(\\', \\') : \\'none\\'}'"
            },
            {
              "icon": "",
              "label": "'Items: none'"
            },
            {
              "icon": "",
              "label": "'Items: a, b' — Template-Literale werten jeden Ausdruck innerhalb von ${...} aus, einschließlich eines vollständigen Ternär, und interpolieren das Ergebnis direkt in den String"
            },
            {
              "icon": "",
              "label": "Es wirft einen SyntaxError, weil Ternäre in Template-Literalen nicht erlaubt sind"
            }
          ]
        },
        {
          "question": "const nums = [1, 2, 3, 4, 5];\nconst result = nums.filter(n => n % 2 === 0).map(n => n * 10).reduce((sum, n) => sum + n, 0);\nconsole.log(result, nums);\nWas wird dies ausgedruckt?",
          "options": [
            {
              "icon": "",
              "label": "60 [1, 2, 3, 4, 5] — filter, map und reduce geben jeweils ein neues Array oder einen Wert zurück, ohne das ursprüngliche Array zu ändern, daher ist nums unverändert, während result die endgültige reduzierte Summe ist (2 und 4, verdoppelt zu 20 und 40, summe zu 60)"
            },
            {
              "icon": "",
              "label": "60 [2, 4, 20, 40]"
            },
            {
              "icon": "",
              "label": "150 [1, 2, 3, 4, 5]"
            },
            {
              "icon": "",
              "label": "60 [20, 40]"
            }
          ]
        }
      ],
      "optionOrderVersion": "50fc0eba818f2d72"
    }
  }
}
