{
  "assessmentTests": {
    "javascript_test": {
      "name": "Tes JavaScript",
      "desc": "30 pertanyaan skenario tentang sintaksis inti dan paksaan tipe, fungsi/ruang lingkup/penutupan dan this, objek/array dan struktur data, dan loop acara/async/idiom prototipe — cari tahu apakah JavaScript Anda cocok dengan apa yang dimaksud perusahaan oleh keahlian JavaScript.",
      "recommendation": "Profil keterampilan JavaScript Anda",
      "results": {
        "beginner": {
          "name": "Pemula",
          "desc": "Anda dapat menulis fungsi dan objek yang berfungsi, tetapi pertanyaan yang Anda lewatkan mengelompok di sekitar paksaan tipe dan semantik referensi daripada sintaksis — == secara diam-diam mengkonversi tipe sebelum membandingkan, penetapan array yang berbagi satu objek daripada menyalinnya, mengapa NaN tidak pernah sama dengan dirinya sendiri. Ini bukan tentang buruk dalam JavaScript; ini adalah aturan spesifik yang membuat orang yang belajar bahasa dengan menulis kode yang kebetulan bekerja daripada belajar bagaimana nilai dan referensi benar-benar berperilaku. Ini penting di tempat kerja karena masing-masing adalah tempat kode berjalan tanpa kesalahan dan masih melakukan hal yang salah.",
          "recommendation": "Mulai dengan tiga hal, dalam urutan ini: mengapa == memaksa tipe sebelum membandingkan sementara === tidak, mengapa menetapkan array atau objek ke variabel baru menyalin referensi daripada nilai, dan bagaimana var diangkat dan diinisialisasi ke undefined sementara let dan const tetap berada di zona mati temporal hingga deklarasi mereka benar-benar berjalan. Panduan JavaScript MDN mencakup ketiga-tiganya dengan contoh yang dapat dijalankan."
        },
        "intermediate": {
          "name": "Menengah",
          "desc": "Anda menangani kode aplikasi sehari-hari dengan nyaman — fungsi, metode array, aliran kontrol yang jelas — dan tidak akan melambat oleh pekerjaan fitur rutin. Kesenjangan antara sini dan Lanjutan adalah sebagian besar tentang apa yang terjadi ketika aturan scoping dan timing JavaScript berinteraksi: var yang ditangkap dalam panggilan balik loop yang semuanya membaca nilai akhir yang sama, this yang diam-diam berhenti menunjuk ke objek begitu metode dilepaskan darinya, salinan spread yang terlihat dalam tetapi hanya menyalin satu level. Ini adalah jenis bug yang lolos dari bacaan cepat dan hanya muncul dalam kondisi waktu eksekusi spesifik.",
          "recommendation": "Fokus pada bagaimana ruang lingkup dan this benar-benar diselesaikan pada waktu panggilan daripada waktu definisi: mengapa loop for berbasis var mengirimkan setiap panggilan balik nilai penghitung akhir yang sama sementara let tidak, bagaimana ini ditentukan oleh cara fungsi dipanggil daripada di mana ia ditulis, dan mengapa spread atau Object.assign hanya menyalin level teratas objek. Kemudian loop acara, karena itulah yang membuat orang yang sudah mengenal janji secara individual terganggu."
        },
        "advanced": {
          "name": "Lanjutan",
          "desc": "Ini adalah level yang dimaksudkan sebagian besar posting pekerjaan dengan \"JavaScript yang kuat\". Anda membaca rantai filter/map/reduce dan dapat memprediksi dengan tepat apa yang dikembalikannya tanpa mengubah array asli, Anda tahu mengapa console.log sinkron selalu mengalahkan microtask yang selalu mengalahkan setTimeout, dan Anda menjangkau Promise.all mengetahui bahwa ia menolak segera ketika janji apa pun melakukan daripada menunggu sisanya. Apa yang memisahkan pita ini dari puncak adalah sisi prototype dan model objek dari pekerjaan: bagaimana rantai prototype menyelesaikan metode warisan versus hasOwnProperty, dan apa yang benar-benar disembunyikan bidang kelas pribadi.",
          "recommendation": "Dorong ke bagian-bagian yang melindungi status yang juga disentuh kode lain: bagaimana rantai prototipe JavaScript menyelesaikan pencarian metode yang tidak ada di instance itu sendiri, apa yang benar-benar disembunyikan #fields pribadi kelas dari akses eksternal, dan bagaimana JSON.stringify memperlakukan undefined dan fungsi secara berbeda di dalam objek biasa versus di dalam array. Materi MDN tentang loop acara dan rantai prototipe adalah langkah berikutnya yang alami untuk keduanya."
        },
        "expert": {
          "name": "Ahli",
          "desc": "Anda mencetak di puncak setiap bagian — sintaksis inti, tipe dan paksaan; fungsi, ruang lingkup, penutupan dan this; objek, array dan struktur data; dan async, loop acara, prototipe dan idiom. Secara praktis, itu berarti Anda dapat diberi fungsi orang asing dan menjelaskan mengapa ia mengembalikan apa yang dikembalikan, bukan hanya apa yang dikatakan sintaksis harus dilakukan, yang merupakan keterampilan yang lebih sulit dan lebih berharga. Pada level ini bahasa itu sendiri jarang menjadi faktor pembatas; batasnya biasanya aliran kontrol async atau bentuk data di bawahnya.",
          "recommendation": "Pengembaliannya sekarang dalam desain dan diagnosis: penalaran tentang kondisi balapan antara Promise.all dan Promise.race daripada mengutip definisi mereka, memilih kapan closure adalah alat yang tepat untuk status pribadi versus kapan bidang pribadi kelas, dan membaca bug pengurutan microtask-vs-macrotask dalam log produksi daripada dalam kuis. Jika Anda disaring untuk peran, jelaskan bug waktu yang Anda temukan — variabel closure basi atau panggilan balik async yang tidak sesuai urutan — daripada menyebutkan fitur JavaScript; ini menunjukkan pemikiran, bukan hanya kosakata."
        }
      },
      "questions": [
        {
          "question": "console.log(x);\nvar x = 5;\nApa yang dicetak ini?",
          "options": [
            {
              "icon": "",
              "label": "5 — deklarasi var berjalan segera, sebelum kode apa pun"
            },
            {
              "icon": "",
              "label": "ReferenceError: x is not defined"
            },
            {
              "icon": "",
              "label": "undefined — deklarasi var diangkat ke puncak ruang lingkup mereka dan diinisialisasi ke undefined, tetapi penugasan dieksekusi di tempat, jadi sebelum berjalan variabel ada tetapi menahan undefined"
            },
            {
              "icon": "",
              "label": "null"
            }
          ]
        },
        {
          "question": "console.log(y);\nlet y = 5;\nApa yang terjadi?",
          "options": [
            {
              "icon": "",
              "label": "Ini melempar ReferenceError: Cannot access 'y' before initialization — let diangkat ke puncak ruang lingkupnya tetapi tetap berada di zona mati temporal hingga garis deklarasi benar-benar dieksekusi"
            },
            {
              "icon": "",
              "label": "Ini mencetak undefined, sama seperti var yang akan dilakukan"
            },
            {
              "icon": "",
              "label": "Ini mencetak 5"
            },
            {
              "icon": "",
              "label": "Ini melempar SyntaxError pada waktu parsing"
            }
          ]
        },
        {
          "question": "const arr = [1, 2, 3];\narr.push(4);\nconsole.log(arr.length);\nApa yang dicetak ini?",
          "options": [
            {
              "icon": "",
              "label": "4 — const hanya mencegah reassign binding arr itu sendiri, bukan mengubah objek array yang ditunjuknya, jadi push masih bekerja"
            },
            {
              "icon": "",
              "label": "Ini melempar TypeError, karena arr adalah const"
            },
            {
              "icon": "",
              "label": "3 — push diblokir pada array const"
            },
            {
              "icon": "",
              "label": "undefined"
            }
          ]
        },
        {
          "question": "console.log('' == 0);\nconsole.log('' === 0);\nApa yang dicetak dua baris ini?",
          "options": [
            {
              "icon": "",
              "label": "true kemudian true"
            },
            {
              "icon": "",
              "label": "false kemudian false"
            },
            {
              "icon": "",
              "label": "true kemudian false — == memaksa string kosong ke 0 sebelum membandingkan, sementara === membandingkan jenis dan nilai tanpa paksaan, dan string tidak pernah benar-benar sama dengan angka"
            },
            {
              "icon": "",
              "label": "false kemudian true"
            }
          ]
        },
        {
          "question": "console.log(NaN === NaN);\nconsole.log(Number.isNaN(NaN));\nApa yang dicetak dua baris ini?",
          "options": [
            {
              "icon": "",
              "label": "true kemudian true"
            },
            {
              "icon": "",
              "label": "true kemudian false"
            },
            {
              "icon": "",
              "label": "false kemudian false"
            },
            {
              "icon": "",
              "label": "false kemudian true — NaN adalah satu-satunya nilai di JavaScript yang tidak sama dengan dirinya sendiri di bawah ===, tetapi Number.isNaN() mendeteksinya dengan benar"
            }
          ]
        },
        {
          "question": "console.log(typeof null);\nApa yang dicetak ini?",
          "options": [
            {
              "icon": "",
              "label": "\"null\""
            },
            {
              "icon": "",
              "label": "\"object\" — bug jangka panjang dalam bahasa: typeof null mengembalikan \"object\" meskipun null adalah primitif, dan itu telah disimpan untuk kompatibilitas mundur sejak saat itu"
            },
            {
              "icon": "",
              "label": "\"undefined\""
            },
            {
              "icon": "",
              "label": "\"boolean\""
            }
          ]
        },
        {
          "question": "Mana dari berikut ini yang truthy ketika digunakan dalam kondisi if?",
          "options": [
            {
              "icon": "",
              "label": "0"
            },
            {
              "icon": "",
              "label": "\"\" (string kosong)"
            },
            {
              "icon": "",
              "label": "null"
            },
            {
              "icon": "",
              "label": "\"0\" — string non-kosong selalu truthy dalam JavaScript, bahkan ketika kontennya adalah string \"0\" itu sendiri"
            }
          ]
        },
        {
          "question": "foo();\nfunction foo() { console.log('a'); }\nbar();\nvar bar = function() { console.log('b'); };\nApa yang terjadi?",
          "options": [
            {
              "icon": "",
              "label": "Kedua panggilan dicetak, \"a\" kemudian \"b\""
            },
            {
              "icon": "",
              "label": "foo() mencetak \"a\"; bar() melempar TypeError: bar is not a function — deklarasi fungsi sepenuhnya diangkat dengan tubuh mereka, tetapi ekspresi fungsi yang ditetapkan var hanya mengangkat var itu sendiri (seperti undefined), bukan penugasan"
            },
            {
              "icon": "",
              "label": "Kedua panggilan melempar ReferenceError"
            },
            {
              "icon": "",
              "label": "foo() melempar; bar() mencetak \"b\""
            }
          ]
        },
        {
          "question": "function makeCounter() {\n  let count = 0;\n  return function() { count++; return count; };\n}\nconst counter = makeCounter();\nconsole.log(counter());\nconsole.log(counter());\nApa yang dicetak dua baris ini?",
          "options": [
            {
              "icon": "",
              "label": "1 kemudian 1"
            },
            {
              "icon": "",
              "label": "0 kemudian 1"
            },
            {
              "icon": "",
              "label": "undefined kemudian undefined"
            },
            {
              "icon": "",
              "label": "1 kemudian 2 — fungsi yang dikembalikan menutup pada variabel count yang sama dari ruang lingkup makeCounter, dan variabel itu bertahan antar panggilan daripada direset"
            }
          ]
        },
        {
          "question": "for (var i = 0; i < 3; i++) {\n  setTimeout(() => console.log(i), 0);\n}\nApa yang dicetak ini?",
          "options": [
            {
              "icon": "",
              "label": "0, 1, 2"
            },
            {
              "icon": "",
              "label": "3, 3, 3 — var bersifat function-scoped, bukan block-scoped, jadi ketiga panggilan balik menutup i yang sama, yang sudah selesai loop dan sama dengan 3 pada saat mana pun berjalan"
            },
            {
              "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}\nApa yang dicetak ini?",
          "options": [
            {
              "icon": "",
              "label": "3, 3, 3"
            },
            {
              "icon": "",
              "label": "0, 0, 0"
            },
            {
              "icon": "",
              "label": "undefined, undefined, undefined"
            },
            {
              "icon": "",
              "label": "0, 1, 2 — let membuat binding i yang segar untuk setiap iterasi loop, jadi setiap panggilan balik menutup salinan terpisahnya sendiri daripada satu variabel bersama"
            }
          ]
        },
        {
          "question": "function greet() { return `Hi, ${this.name}`; }\nconst person = { name: 'Ana' };\nconsole.log(greet.call(person));\nApa yang dicetak ini?",
          "options": [
            {
              "icon": "",
              "label": "\"Hi, Ana\" — Function.prototype.call menjalankan fungsi dengan this secara eksplisit diatur ke argumen pertamanya, jadi this.name menyelesaikan melawan person"
            },
            {
              "icon": "",
              "label": "\"Hi, undefined\" — call() hanya bekerja pada fungsi yang sudah merupakan metode objek"
            },
            {
              "icon": "",
              "label": "Ini melempar TypeError, karena greet bukan metode person"
            },
            {
              "icon": "",
              "label": "\"Hi, ${this.name}\" — call() tidak mengevaluasi template literal"
            }
          ]
        },
        {
          "question": "const timer = {\n  seconds: 0,\n  start: function() {\n    setInterval(() => { this.seconds++; }, 1000);\n  },\n};\nMengapa orang lebih suka fungsi arrow daripada function() { this.seconds++ } di dalam setInterval di sini?",
          "options": [
            {
              "icon": "",
              "label": "Fungsi panah tidak memiliki this mereka sendiri — mereka menangkap this secara leksikal dari fungsi start yang melingkupi, yang merupakan timer (karena start dipanggil sebagai timer.start()), jadi this.seconds dengan benar merujuk ke timer.seconds di setiap tick"
            },
            {
              "icon": "",
              "label": "Fungsi panah dieksekusi lebih cepat daripada fungsi reguler pada waktu runtime"
            },
            {
              "icon": "",
              "label": "Fungsi panah secara otomatis mengikat this ke objek global, yang kebetulan sama dengan timer"
            },
            {
              "icon": "",
              "label": "Fungsi panah mengonversi this menjadi variabel global secara otomatis"
            }
          ]
        },
        {
          "question": "function multiply(a, b) { return a * b; }\nconst double = multiply.bind(null, 2);\nconsole.log(double(5));\nApa yang dicetak ini?",
          "options": [
            {
              "icon": "",
              "label": "Ini melempar kesalahan, karena bind memerlukan kedua argumen di muka"
            },
            {
              "icon": "",
              "label": "10 — bind mengembalikan fungsi baru dengan this dan argumen kepala apa pun yang telah diatur sebelumnya; memanggil double(5) menyediakan argumen yang tersisa b=5, jadi multiply(2, 5) berjalan"
            },
            {
              "icon": "",
              "label": "NaN"
            },
            {
              "icon": "",
              "label": "7, karena bind menambahkan argumennya ke panggilan daripada menggantinya"
            }
          ]
        },
        {
          "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();\nApa yang dicetak oleh dua panggilan console.log?",
          "options": [
            {
              "icon": "",
              "label": "20 kemudian 20"
            },
            {
              "icon": "",
              "label": "20 kemudian 10 — let x milik inner membayangkan x milik outer hanya di dalam ruang lingkup inner; x milik outer tidak pernah disentuh"
            },
            {
              "icon": "",
              "label": "10 kemudian 10"
            },
            {
              "icon": "",
              "label": "Ini melempar ReferenceError, karena x dideklarasikan dua kali"
            }
          ]
        },
        {
          "question": "const a = [1, 2, 3];\nconst b = a;\nb.push(4);\nconsole.log(a.length);\nApa yang dicetak ini?",
          "options": [
            {
              "icon": "",
              "label": "3 — b adalah salinan terpisah dari a"
            },
            {
              "icon": "",
              "label": "undefined"
            },
            {
              "icon": "",
              "label": "Ini melempar TypeError, karena a adalah const"
            },
            {
              "icon": "",
              "label": "4 — array adalah objek, jadi const b = a menyalin referensi, bukan array; mengubah b melalui push juga mengubah array yang sama persis yang ditunjuk a"
            }
          ]
        },
        {
          "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);\nApa yang dicetak ini?",
          "options": [
            {
              "icon": "",
              "label": "'Ana' 2 — spread deep-clones setiap objek dan array bersarang"
            },
            {
              "icon": "",
              "label": "'Ana' 3 — spread hanya salinan dangkal: properti primitif seperti name disalin berdasarkan nilai, jadi reassign copy.name tidak pernah menyentuh original, tetapi array bersarang seperti tags disalin berdasarkan referensi, jadi push ke copy.tags mengubah array yang sama persis yang ditunjuk original.tags"
            },
            {
              "icon": "",
              "label": "'Bea' 3 — spread menyalin semua per referensi, jadi perubahan tingkat atas dan bersarang kembali ke original"
            },
            {
              "icon": "",
              "label": "'Bea' 2"
            }
          ]
        },
        {
          "question": "const { a: x = 10, b: y = 20 } = { a: 5 };\nconsole.log(x, y);\nApa yang dicetak ini?",
          "options": [
            {
              "icon": "",
              "label": "undefined undefined"
            },
            {
              "icon": "",
              "label": "5 undefined"
            },
            {
              "icon": "",
              "label": "10 20"
            },
            {
              "icon": "",
              "label": "5 20 — a ditafsirkan ulang menjadi x dan hadir, jadi tetap nilai 5; b ditafsirkan ulang menjadi y dan tidak ada, jadi kembali ke default 20"
            }
          ]
        },
        {
          "question": "const [first, ...rest] = [1, 2, 3, 4];\nconsole.log(rest);\nApa yang dicetak ini?",
          "options": [
            {
              "icon": "",
              "label": "[1, 2, 3, 4]"
            },
            {
              "icon": "",
              "label": "[2, 3, 4] — rest mengumpulkan setiap elemen yang tersisa setelah first ke dalam array baru"
            },
            {
              "icon": "",
              "label": "2"
            },
            {
              "icon": "",
              "label": "[1]"
            }
          ]
        },
        {
          "question": "const nums = [10, 1, 2];\nnums.sort();\nconsole.log(nums);\nApa yang dicetak ini?",
          "options": [
            {
              "icon": "",
              "label": "[1, 2, 10] — sort selalu mengurutkan angka secara numerik dalam urutan ascending secara default"
            },
            {
              "icon": "",
              "label": "[10, 1, 2] — sort tidak mengubah array asli"
            },
            {
              "icon": "",
              "label": "[1, 10, 2] — pembanding default Array.prototype.sort mengkonversi elemen menjadi string dan membandingkannya secara leksikal, jadi \"10\" mengurutkan sebelum \"2\"; sort juga mengubah array di tempat daripada mengembalikan array baru"
            },
            {
              "icon": "",
              "label": "[2, 1, 10]"
            }
          ]
        },
        {
          "question": "const user = { profile: { age: 0 } };\nconsole.log(user.profile?.age ?? 'unknown');\nconsole.log(user.address?.city ?? 'unknown');\nApa yang dicetak dua baris ini?",
          "options": [
            {
              "icon": "",
              "label": "'unknown' kemudian 'unknown' — ?? memperlakukan 0 sebagai hilang, dengan cara yang sama || lakukan"
            },
            {
              "icon": "",
              "label": "0 kemudian 'unknown' — ?? hanya kembali pada null atau undefined, bukan pada nilai falsy lainnya seperti 0, jadi age (0) disimpan apa adanya; user.address adalah undefined, jadi ?. pendek-sirkuit seluruh rantai ke undefined dan ?? kemudian menyediakan 'unknown'"
            },
            {
              "icon": "",
              "label": "0 kemudian undefined"
            },
            {
              "icon": "",
              "label": "'unknown' kemudian 0"
            }
          ]
        },
        {
          "question": "const obj = { a: undefined, b: function(){}, c: NaN, d: [undefined, function(){}, 1] };\nconsole.log(JSON.stringify(obj));\nApa yang dicetak ini?",
          "options": [
            {
              "icon": "",
              "label": "'{\"a\":null,\"b\":null,\"c\":null,\"d\":[null,null,1]}' — setiap nilai yang tidak didukung menjadi null di mana-mana, dalam objek dan array"
            },
            {
              "icon": "",
              "label": "'{\"a\":undefined,\"b\":undefined,\"c\":NaN,\"d\":[undefined,null,1]}'"
            },
            {
              "icon": "",
              "label": "'{\"c\":null,\"d\":[null,null,1]}' — properti objek yang nilainya undefined atau fungsi dihilangkan sepenuhnya, dan NaN serialisasi ke null, tetapi di dalam array nilai-nilai yang tidak didukung yang sama menjadi null bukan dijatuhkan, karena menghapus elemen array akan menggeser setiap indeks yang lebih besar"
            },
            {
              "icon": "",
              "label": "Ini melempar TypeError, karena NaN tidak dapat diserialisasi ke JSON"
            }
          ]
        },
        {
          "question": "console.log('1');\nsetTimeout(() => console.log('2'), 0);\nPromise.resolve().then(() => console.log('3'));\nconsole.log('4');\nDalam urutan berapa ini dicetak?",
          "options": [
            {
              "icon": "",
              "label": "1, 2, 3, 4"
            },
            {
              "icon": "",
              "label": "1, 4, 2, 3"
            },
            {
              "icon": "",
              "label": "1, 4, 3, 2 — kode sinkron berjalan terlebih dahulu (jadi 1 dan 4 dicetak segera), kemudian antrian microtask (panggilan balik Promise) menguras sepenuhnya sebelum loop acara beralih ke macrotask berikutnya, jadi 3 dicetak sebelum panggilan balik setTimeout dari 2"
            },
            {
              "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');\nDalam urutan berapa ini dicetak?",
          "options": [
            {
              "icon": "",
              "label": "start, a, end, b — foo() berjalan secara sinkron hingga await pertamanya, jadi 'a' dicetak segera ketika foo() dipanggil; await kemudian menangguhkan foo dan mengembalikan kontrol ke pemanggil, jadi 'end' dicetak sebelum 'b' yang dilanjutkan, yang berjalan nanti sebagai microtask"
            },
            {
              "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));\nApa yang dicetak ini?",
          "options": [
            {
              "icon": "",
              "label": "'fail err' — Promise.all menolak segera ketika salah satu janji masukan menolak, mengabaikan hasil yang lain, jadi penanganan catch berjalan dengan alasan penolakan itu"
            },
            {
              "icon": "",
              "label": "'ok' [1, 3]"
            },
            {
              "icon": "",
              "label": "'ok' [1, undefined, 3]"
            },
            {
              "icon": "",
              "label": "Ini melempar secara sinkron dan tidak ada penanganan yang pernah berjalan"
            }
          ]
        },
        {
          "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));\nApa yang dicetak ini?",
          "options": [
            {
              "icon": "",
              "label": "'slow'"
            },
            {
              "icon": "",
              "label": "['fast', 'slow']"
            },
            {
              "icon": "",
              "label": "'fast' — Promise.race menyelesaikan segera ketika yang pertama dari masukan janji menyelesaikan, dan 10ms timer menyelesaikan jauh sebelum yang 100ms"
            },
            {
              "icon": "",
              "label": "Ini melempar kesalahan, karena race memerlukan tepat dua janji"
            }
          ]
        },
        {
          "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'));\nApa yang dicetak dua baris ini?",
          "options": [
            {
              "icon": "",
              "label": "'Rex makes a sound' kemudian true"
            },
            {
              "icon": "",
              "label": "undefined kemudian false"
            },
            {
              "icon": "",
              "label": "Ini melempar TypeError, karena dog tidak memiliki metode speak sendiri"
            },
            {
              "icon": "",
              "label": "'Rex makes a sound' kemudian false — speak didefinisikan pada Animal.prototype, bukan pada instance dog itu sendiri; dog menemukannya melalui rantai prototipe saat dipanggil, tetapi hasOwnProperty hanya memeriksa properti instance itu sendiri, bukan apa pun yang diwarisi"
            }
          ]
        },
        {
          "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);\nApa yang dicetak dua baris ini?",
          "options": [
            {
              "icon": "",
              "label": "1 kemudian undefined — #count adalah bidang kelas pribadi, dapat dibaca dan ditulisi hanya dari dalam metode kelas itu sendiri; c.count (tanpa #) adalah properti biasa yang sepenuhnya berbeda yang tidak pernah diatur, jadi tidak terdefinisi"
            },
            {
              "icon": "",
              "label": "1 kemudian 0"
            },
            {
              "icon": "",
              "label": "1 kemudian 1"
            },
            {
              "icon": "",
              "label": "Ini melempar SyntaxError saat mengakses c.count"
            }
          ]
        },
        {
          "question": "const items = ['a', 'b'];\nconst label = `Items: ${items.length > 0 ? items.join(', ') : 'none'}`;\nconsole.log(label);\nApa yang dicetak ini?",
          "options": [
            {
              "icon": "",
              "label": "'Items: ${items.length > 0 ? items.join(\\', \\') : \\'none\\'}'"
            },
            {
              "icon": "",
              "label": "'Items: none'"
            },
            {
              "icon": "",
              "label": "'Items: a, b' — template literal mengevaluasi ekspresi apa pun yang ditempatkan di dalam ${...}, termasuk ternary lengkap, dan menginterpolasi hasilnya langsung ke dalam string"
            },
            {
              "icon": "",
              "label": "Ini melempar SyntaxError, karena ternaries tidak diizinkan di dalam template literal"
            }
          ]
        },
        {
          "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);\nApa yang dicetak ini?",
          "options": [
            {
              "icon": "",
              "label": "60 [1, 2, 3, 4, 5] — filter, map dan reduce masing-masing mengembalikan array atau nilai baru tanpa mengubah array asli, jadi nums tidak berubah sementara result adalah jumlah terakhir yang dikurangi (2 dan 4, digandakan menjadi 20 dan 40, dijumlahkan menjadi 60)"
            },
            {
              "icon": "",
              "label": "60 [2, 4, 20, 40]"
            },
            {
              "icon": "",
              "label": "150 [1, 2, 3, 4, 5]"
            },
            {
              "icon": "",
              "label": "60 [20, 40]"
            }
          ]
        }
      ],
      "optionOrderVersion": "50fc0eba818f2d72"
    }
  }
}
