{
  "assessmentTests": {
    "javascript_test": {
      "name": "Bài Kiểm Tra JavaScript",
      "desc": "30 câu hỏi tình huống về cú pháp cơ bản và ép kiểu, hàm/phạm vi/closure và this, đối tượng/mảng và cấu trúc dữ liệu, và vòng lặp sự kiện/async/prototype - tìm hiểu liệu kiến thức JavaScript của bạn có phù hợp với ý nghĩa của nhà tuyển dụng về kỹ năng JavaScript hay không.",
      "recommendation": "Hồ sơ kỹ năng JavaScript của bạn",
      "results": {
        "beginner": {
          "name": "Người Bắt Đầu",
          "desc": "Bạn có thể viết các hàm và đối tượng hoạt động, nhưng các câu hỏi bạn bỏ qua tập trung xung quanh ép kiểu và ngữ nghĩa tham chiếu thay vì cú pháp — == âm thầm chuyển đổi loại trước khi so sánh, gán mảng chia sẻ một đối tượng thay vì sao chép nó, tại sao NaN không bao giờ bằng chính nó. Không có điều nào trong số này là về việc tệ ở JavaScript; đây là những quy tắc cụ thể làm bối rối những người đã học ngôn ngữ bằng cách viết mã xảy ra hoạt động thay vì bằng cách học cách các giá trị và tham chiếu thực sự hoạt động. Chúng quan trọng ở công việc vì mỗi một là nơi mã chạy mà không có lỗi và vẫn làm sai.",
          "recommendation": "Bắt đầu với ba điều, theo thứ tự này: tại sao == ép kiểu trước khi so sánh trong khi === thì không, tại sao gán một mảng hoặc đối tượng cho một biến mới sao chép tham chiếu thay vì giá trị, và cách var được nâng lên và khởi tạo thành undefined trong khi let và const ở lại trong vùng chết tạm thời cho đến khi khai báo của chúng chạy. Hướng dẫn JavaScript trên MDN bao gồm cả ba với các ví dụ có thể chạy được."
        },
        "intermediate": {
          "name": "Trung Bình",
          "desc": "Bạn xử lý mã ứng dụng hàng ngày một cách thoải mái — hàm, phương thức mảng, luồng kiểm soát thẳng — và sẽ không bị làm chậm bởi công việc tính năng thường xuyên. Khoảng cách giữa đây và Nâng cao chủ yếu là trong những gì xảy ra khi các quy tắc phạm vi và thời gian của JavaScript tương tác: var được chụp bên trong các lệnh gọi lại của vòng lặp đều đọc cùng một giá trị cuối cùng, một cái this âm thầm ngừng chỉ vào đối tượng khi phương thức bị tách ra khỏi nó, một bản sao mở rộng trông sâu nhưng chỉ sao chép một cấp. Đây là loại lỗi vượt qua đọc nhanh chóng và chỉ xuất hiện dưới một điều kiện thời gian chạy cụ thể.",
          "recommendation": "Tập trung vào cách phạm vi và this thực sự giải quyết tại thời điểm gọi chứ không phải lúc xác định: tại sao vòng lặp for dựa trên var trao cho mỗi lệnh gọi lại cùng một giá trị bộ đếm cuối cùng trong khi let thì không, cách this được xác định bởi cách gọi hàm thay vì nơi nó được viết, và tại sao sự trải rộng hoặc Object.assign chỉ sao chép cấp trên cùng của một đối tượng. Sau đó là vòng lặp sự kiện, vì đó là những gì làm bối rối những người đã biết lời hứa riêng lẻ."
        },
        "advanced": {
          "name": "Nâng Cao",
          "desc": "Đây là mức độ hầu hết các bài đăng công việc có ý nghĩa bởi 'JavaScript mạnh mẽ'. Bạn đọc một chuỗi filter/map/reduce và có thể dự đoán chính xác những gì nó trả về mà không thay đổi mảng gốc, bạn biết tại sao console.log đồng bộ luôn đánh bại microtask luôn đánh bại setTimeout, và bạn sử dụng Promise.all biết nó từ chối ngay lập tức bất kỳ lời hứa nào từ chối thay vì chờ phần còn lại. Điều phân tách band này từ đầu là phía mô hình prototype và đối tượng của công việc: cách chuỗi prototype giải quyết phương thức được thừa kế so với hasOwnProperty, và trường lớp riêng tư thực sự ẩn cái gì.",
          "recommendation": "Đẩy vào các phần bảo vệ trạng thái mã khác cũng chạm vào: cách chuỗi prototype JavaScript giải quyết tìm kiếm phương thức không có trên chính thể hiện, trường #fields của lớp thực sự ẩn từ truy cập bên ngoài cái gì, và cách JSON.stringify xử lý undefined và hàm khác nhau bên trong đối tượng thuần túy so với bên trong mảng. Tài liệu MDN về vòng lặp sự kiện và chuỗi prototype là bước tiếp theo tự nhiên cho cả hai."
        },
        "expert": {
          "name": "Chuyên Gia",
          "desc": "Bạn ghi điểm cao nhất ở mọi phần — cú pháp cơ bản, loại và ép kiểu; hàm, phạm vi, closure và this; đối tượng, mảng và cấu trúc dữ liệu; và async, vòng lặp sự kiện, prototype và thành ngữ. Thực tế, điều đó có nghĩa là bạn có thể được trao một hàm của người lạ và giải thích tại sao nó trả về những gì nó trả về, không chỉ là những gì cú pháp nói nó phải làm, đó là kỹ năng khó hơn và có giá trị hơn. Ở mức độ này, ngôn ngữ tự nó hiếm khi là yếu tố hạn chế; giới hạn thường là luồng kiểm soát không đồng bộ hoặc hình dạng dữ liệu dưới nó.",
          "recommendation": "Những lợi nhuận hiện tại ở thiết kế và chẩn đoán: lý luận về một điều kiện chạy đua giữa Promise.all và Promise.race thay vì trích dẫn định nghĩa của chúng, chọn khi closure là công cụ phù hợp cho trạng thái riêng tư so với khi trường lớp riêng tư là, và đọc lỗi thứ tự microtask-vs-macrotask trong nhật ký sản xuất thay vì trong bài kiểm tra. Nếu bạn đang được sàng lọc cho một vai trò, hãy mô tả lỗi thời gian bạn tìm thấy — một biến closure cũ hoặc lệnh gọi lại không đồng bộ ngoài thứ tự — thay vì đặt tên các tính năng JavaScript; nó chứng tỏ lý luận, không chỉ từ vựng."
        }
      },
      "questions": [
        {"question": "console.log(x);\nvar x = 5;\nCái này in gì?", "options": [{"icon": "", "label": "5 — khai báo var chạy ngay lập tức, trước bất kỳ mã nào khác"}, {"icon": "", "label": "ReferenceError: x không được xác định"}, {"icon": "", "label": "undefined — khai báo var được nâng lên đầu phạm vi của chúng và khởi tạo thành undefined, nhưng phép gán chạy tại chỗ, vì vậy trước khi nó chạy biến tồn tại nhưng giữ undefined"}, {"icon": "", "label": "null"}]},
        {"question": "console.log(y);\nlet y = 5;\nCái gì xảy ra?", "options": [{"icon": "", "label": "Nó ném ReferenceError: Không thể truy cập 'y' trước khi khởi tạo — let được nâng lên đầu phạm vi của nó nhưng ở lại trong vùng chết tạm thời cho đến khi dòng khai báo thực sự chạy"}, {"icon": "", "label": "Nó in undefined, giống như var"}, {"icon": "", "label": "Nó in 5"}, {"icon": "", "label": "Nó ném SyntaxError vào lúc phân tích cú pháp"}]},
        {"question": "const arr = [1, 2, 3];\narr.push(4);\nconsole.log(arr.length);\nCái này in gì?", "options": [{"icon": "", "label": "4 — const chỉ ngăn chặn gán lại arr binding chính nó, không phải đối tượng mảng nó chỉ vào, vì vậy push vẫn hoạt động"}, {"icon": "", "label": "Nó ném TypeError, vì arr là const"}, {"icon": "", "label": "3 — push bị chặn trên mảng const"}, {"icon": "", "label": "undefined"}]},
        {"question": "console.log('' == 0);\nconsole.log('' === 0);\nHai dòng in gì?", "options": [{"icon": "", "label": "true rồi true"}, {"icon": "", "label": "false rồi false"}, {"icon": "", "label": "true rồi false — == ép chuỗi trống thành 0 trước khi so sánh, trong khi === so sánh loại và giá trị mà không ép kiểu, và chuỗi không bao giờ bằng một số"}, {"icon": "", "label": "false rồi true"}]},
        {"question": "console.log(NaN === NaN);\nconsole.log(Number.isNaN(NaN));\nHai dòng in gì?", "options": [{"icon": "", "label": "true rồi true"}, {"icon": "", "label": "true rồi false"}, {"icon": "", "label": "false rồi false"}, {"icon": "", "label": "false rồi true — NaN là giá trị duy nhất trong JavaScript không bằng chính nó dưới ===, nhưng Number.isNaN() vẫn phát hiện nó một cách chính xác"}]},
        {"question": "console.log(typeof null);\nCái này in gì?", "options": [{"icon": "", "label": "\"null\""}, {"icon": "", "label": "\"object\" — lỗi lâu đời của ngôn ngữ: typeof null trả về \"object\" mặc dù null là nguyên thủy, và nó đã được giữ lại để tương thích ngược kể từ đó"}, {"icon": "", "label": "\"undefined\""}, {"icon": "", "label": "\"boolean\""}]},
        {"question": "Cái nào sau đây là sự thật khi sử dụng trong điều kiện if?", "options": [{"icon": "", "label": "0"}, {"icon": "", "label": "\"\" (chuỗi trống)"}, {"icon": "", "label": "null"}, {"icon": "", "label": "\"0\" — chuỗi không trống luôn đúng trong JavaScript, ngay cả khi nội dung của nó là chính chuỗi \"0\""}]},
        {"question": "foo();\nfunction foo() { console.log('a'); }\nbar();\nvar bar = function() { console.log('b'); };\nCái gì xảy ra?", "options": [{"icon": "", "label": "Cả hai cuộc gọi in, \"a\" rồi \"b\""}, {"icon": "", "label": "foo() in \"a\"; bar() ném TypeError: bar không phải là hàm — khai báo hàm được nâng lên hoàn toàn với cơ thể của chúng, nhưng biểu thức hàm gán var chỉ nâng lên var chính nó (như undefined), không phải phép gán"}, {"icon": "", "label": "Cả hai cuộc gọi ném ReferenceError"}, {"icon": "", "label": "foo() ném; bar() in \"b\""}]},
        {"question": "function makeCounter() {\n  let count = 0;\n  return function() { count++; return count; };\n}\nconst counter = makeCounter();\nconsole.log(counter());\nconsole.log(counter());\nHai dòng in gì?", "options": [{"icon": "", "label": "1 rồi 1"}, {"icon": "", "label": "0 rồi 1"}, {"icon": "", "label": "undefined rồi undefined"}, {"icon": "", "label": "1 rồi 2 — hàm được trả về đóng trên cùng một biến count từ phạm vi makeCounter, và biến đó tồn tại giữa các lệnh gọi thay vì được đặt lại"}]},
        {"question": "for (var i = 0; i < 3; i++) {\n  setTimeout(() => console.log(i), 0);\n}\nCái này in gì?", "options": [{"icon": "", "label": "0, 1, 2"}, {"icon": "", "label": "3, 3, 3 — var có phạm vi hàm, không phải khối, vì vậy cả ba lệnh gọi lại đóng trên cùng i, đã hoàn thành vòng lặp và bằng 3 vào lúc chúng thực sự chạy"}, {"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}\nCái này in gì?", "options": [{"icon": "", "label": "3, 3, 3"}, {"icon": "", "label": "0, 0, 0"}, {"icon": "", "label": "undefined, undefined, undefined"}, {"icon": "", "label": "0, 1, 2 — let tạo một ràng buộc mới của i cho mỗi lần lặp vòng lặp, vì vậy mỗi lệnh gọi lại đóng trên bản sao riêng của nó thay vì một biến chung"}]},
        {"question": "function greet() { return `Hi, ${this.name}`; }\nconst person = { name: 'Ana' };\nconsole.log(greet.call(person));\nCái này in gì?", "options": [{"icon": "", "label": "\"Hi, Ana\" — Function.prototype.call gọi hàm với this được đặt rõ ràng thành đối số đầu tiên của nó, vì vậy this.name giải quyết dựa trên person"}, {"icon": "", "label": "\"Hi, undefined\" — call() chỉ hoạt động trên các hàm đã là phương thức của một đối tượng"}, {"icon": "", "label": "Nó ném TypeError, vì greet không phải là phương thức của person"}, {"icon": "", "label": "\"Hi, ${this.name}\" — call() không đánh giá ký tự mẫu"}]},
        {"question": "const timer = {\n  seconds: 0,\n  start: function() {\n    setInterval(() => { this.seconds++; }, 1000);\n  },\n};\nTại sao mọi người lại thích hàm mũi tên hơn function() { this.seconds++ } bên trong setInterval ở đây?", "options": [{"icon": "", "label": "Hàm mũi tên không có this riêng của chúng — chúng ghi lại this từ hàm bao quanh start, là timer (vì start được gọi là timer.start()), vì vậy this.seconds chính xác tham chiếu đến timer.seconds trên mỗi tick"}, {"icon": "", "label": "Hàm mũi tên thực thi nhanh hơn hàm thông thường khi chạy"}, {"icon": "", "label": "Hàm mũi tên tự động ràng buộc this với đối tượng toàn cầu, tình cờ bằng timer"}, {"icon": "", "label": "Hàm mũi tên tự động chuyển this thành biến toàn cầu"}]},
        {"question": "function multiply(a, b) { return a * b; }\nconst double = multiply.bind(null, 2);\nconsole.log(double(5));\nCái này in gì?", "options": [{"icon": "", "label": "Nó ném lỗi, vì bind yêu cầu cả hai đối số trước"}, {"icon": "", "label": "10 — bind trả về một hàm mới với this và bất kỳ đối số hàng đầu nào được đặt trước; gọi double(5) cung cấp đối số còn lại b=5, vì vậy multiply(2, 5) chạy"}, {"icon": "", "label": "NaN"}, {"icon": "", "label": "7, vì bind thêm các đối số của nó vào cuộc gọi thay vì thay thế chúng"}]},
        {"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();\nHai lệnh gọi console.log in gì?", "options": [{"icon": "", "label": "20 rồi 20"}, {"icon": "", "label": "20 rồi 10 — let x của inner che bóng x của outer chỉ bên trong phạm vi inner; x của outer không bao giờ bị chạm vào"}, {"icon": "", "label": "10 rồi 10"}, {"icon": "", "label": "Nó ném ReferenceError, vì x được khai báo hai lần"}]},
        {"question": "const a = [1, 2, 3];\nconst b = a;\nb.push(4);\nconsole.log(a.length);\nCái này in gì?", "options": [{"icon": "", "label": "3 — b là một bản sao riêng biệt của a"}, {"icon": "", "label": "undefined"}, {"icon": "", "label": "Nó ném TypeError, vì a là const"}, {"icon": "", "label": "4 — mảng là đối tượng, vì vậy const b = a sao chép tham chiếu, không phải mảng; đột biến b thông qua push cũng đột biến mảng chính xác mà a chỉ vào"}]},
        {"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);\nCái này in gì?", "options": [{"icon": "", "label": "'Ana' 2 — spread sao chép sâu mọi đối tượng và mảng lồng nhau"}, {"icon": "", "label": "'Ana' 3 — spread chỉ là bản sao nông: thuộc tính nguyên thủy như name được sao chép theo giá trị, vì vậy gán lại copy.name không bao giờ chạm vào original, nhưng mảng lồng nhau như tags được sao chép theo tham chiếu, vì vậy push vào copy.tags đột biến mảng chính xác mà original.tags chỉ vào"}, {"icon": "", "label": "'Bea' 3 — spread sao chép mọi thứ theo tham chiếu, vì vậy cả thay đổi đầu tiên và lồng nhau đều được đẩy trở lại original"}, {"icon": "", "label": "'Bea' 2"}]},
        {"question": "const { a: x = 10, b: y = 20 } = { a: 5 };\nconsole.log(x, y);\nCái này in gì?", "options": [{"icon": "", "label": "undefined undefined"}, {"icon": "", "label": "5 undefined"}, {"icon": "", "label": "10 20"}, {"icon": "", "label": "5 20 — a được đổi tên thành x và có mặt, vì vậy nó giữ giá trị 5; b được đổi tên thành y và vắng mặt, vì vậy nó quay lại mặc định 20"}]},
        {"question": "const [first, ...rest] = [1, 2, 3, 4];\nconsole.log(rest);\nCái này in gì?", "options": [{"icon": "", "label": "[1, 2, 3, 4]"}, {"icon": "", "label": "[2, 3, 4] — rest thu thập mọi phần tử còn lại sau first vào một mảng hoàn toàn mới"}, {"icon": "", "label": "2"}, {"icon": "", "label": "[1]"}]},
        {"question": "const nums = [10, 1, 2];\nnums.sort();\nconsole.log(nums);\nCái này in gì?", "options": [{"icon": "", "label": "[1, 2, 10] — sort luôn sắp xếp các số theo thứ tự tăng dần theo mặc định"}, {"icon": "", "label": "[10, 1, 2] — sort không đột biến mảng gốc"}, {"icon": "", "label": "[1, 10, 2] — trình so sánh mặc định của Array.prototype.sort chuyển đổi các phần tử thành chuỗi và so sánh chúng từ tự, vì vậy \"10\" sắp xếp trước \"2\"; sort cũng đột biến mảy tại chỗ thay vì trả về một cái mới"}, {"icon": "", "label": "[2, 1, 10]"}]},
        {"question": "const user = { profile: { age: 0 } };\nconsole.log(user.profile?.age ?? 'unknown');\nconsole.log(user.address?.city ?? 'unknown');\nHai dòng in gì?", "options": [{"icon": "", "label": "'unknown' rồi 'unknown' — ?? coi 0 là không có, như || làm"}, {"icon": "", "label": "0 rồi 'unknown' — ?? chỉ quay lại trên null hoặc undefined, không phải giá trị sai khác như 0, vì vậy age (0) được giữ nguyên; user.address là undefined, vì vậy ?. đóng chuỗi toàn bộ thành undefined và ?? sau đó cung cấp 'unknown'"}, {"icon": "", "label": "0 rồi undefined"}, {"icon": "", "label": "'unknown' rồi 0"}]},
        {"question": "const obj = { a: undefined, b: function(){}, c: NaN, d: [undefined, function(){}, 1] };\nconsole.log(JSON.stringify(obj));\nCái này in gì?", "options": [{"icon": "", "label": "'{\"a\":null,\"b\":null,\"c\":null,\"d\":[null,null,1]}' — mọi giá trị không được hỗ trợ trở thành null ở mọi nơi, trong đối tượng và mảng giống nhau"}, {"icon": "", "label": "'{\"a\":undefined,\"b\":undefined,\"c\":NaN,\"d\":[undefined,null,1]}'"}, {"icon": "", "label": "'{\"c\":null,\"d\":[null,null,1]}' — thuộc tính đối tượng có giá trị là undefined hoặc hàm bị bỏ qua hoàn toàn, và NaN được hợp nhất thành null, nhưng bên trong mảng những giá trị không được hỗ trợ tương tự trở thành null thay vì bị loại bỏ, bởi vì xóa phần tử mảng sẽ dịch chuyển mọi chỉ mục sau đó"}, {"icon": "", "label": "Nó ném TypeError, vì NaN không thể được hợp nhất thành JSON"}]},
        {"question": "console.log('1');\nsetTimeout(() => console.log('2'), 0);\nPromise.resolve().then(() => console.log('3'));\nconsole.log('4');\nChúng được in theo thứ tự nào?", "options": [{"icon": "", "label": "1, 2, 3, 4"}, {"icon": "", "label": "1, 4, 2, 3"}, {"icon": "", "label": "1, 4, 3, 2 — mã đồng bộ chạy trước (vì vậy 1 và 4 được in ngay lập tức), sau đó hàng đợi microtask được thoát hoàn toàn trước khi vòng lặp sự kiện chuyển sang macrotask tiếp theo, vì vậy 3 được in trước 2 của lệnh gọi lại setTimeout"}, {"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');\nChúng được in theo thứ tự nào?", "options": [{"icon": "", "label": "start, a, end, b — foo() chạy đồng bộ cho đến await đầu tiên của nó, vì vậy 'a' được ghi lại ngay lập tức khi foo() được gọi; await sau đó tạm dừng foo và trả lại kiểm soát cho người gọi, vì vậy 'end' được ghi lại trước 'b' tiếp tục, chạy sau đó dưới dạng 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));\nCái này in gì?", "options": [{"icon": "", "label": "'fail err' — Promise.all từ chối ngay lập tức khi bất kỳ lời hứa nào từ chối, bỏ qua kết quả của những cái khác, vì vậy trình xử lý catch chạy với lý do từ chối đó"}, {"icon": "", "label": "'ok' [1, 3]"}, {"icon": "", "label": "'ok' [1, undefined, 3]"}, {"icon": "", "label": "Nó ném đồng bộ và không có trình xử lý nào chạy"}]},
        {"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));\nCái này in gì?", "options": [{"icon": "", "label": "'slow'"}, {"icon": "", "label": "['fast', 'slow']"}, {"icon": "", "label": "'fast' — Promise.race giải quyết ngay khi lời hứa đầu tiên của nó giải quyết, và bộ đếm thời gian 10ms giải quyết lâu trước bộ đếm 100ms"}, {"icon": "", "label": "Nó ném lỗi, vì race yêu cầu chính xác hai lời hứa"}]},
        {"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'));\nHai dòng in gì?", "options": [{"icon": "", "label": "'Rex makes a sound' rồi true"}, {"icon": "", "label": "undefined rồi false"}, {"icon": "", "label": "Nó ném TypeError, vì dog không có phương thức speak của riêng nó"}, {"icon": "", "label": "'Rex makes a sound' rồi false — speak được xác định trên Animal.prototype, không phải trên thể hiện dog; dog tìm thấy nó thông qua chuỗi prototype khi nó được gọi, nhưng hasOwnProperty chỉ kiểm tra các thuộc tính của chính thể hiện, không phải bất cứ thứ gì được kế thừa"}]},
        {"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);\nHai dòng in gì?", "options": [{"icon": "", "label": "1 rồi undefined — #count là trường lớp riêng tư, có thể đọc và ghi chỉ từ bên trong các phương thức của chính lớp; c.count (không #) là thuộc tính thông thường hoàn toàn khác, không bao giờ được đặt, vì vậy nó là undefined"}, {"icon": "", "label": "1 rồi 0"}, {"icon": "", "label": "1 rồi 1"}, {"icon": "", "label": "Nó ném SyntaxError khi truy cập c.count"}]},
        {"question": "const items = ['a', 'b'];\nconst label = `Items: ${items.length > 0 ? items.join(', ') : 'none'}`;\nconsole.log(label);\nCái này in gì?", "options": [{"icon": "", "label": "'Items: ${items.length > 0 ? items.join(\\', \\') : \\'none\\'}'"}, {"icon": "", "label": "'Items: none'"}, {"icon": "", "label": "'Items: a, b' — ký tự mẫu đánh giá bất kỳ biểu thức nào được đặt bên trong ${...}, bao gồm một bộ ba đầy đủ, và nội suy kết quả của nó trực tiếp vào chuỗi"}, {"icon": "", "label": "Nó ném SyntaxError, vì bộ ba không được phép bên trong ký tự mẫu"}]},
        {"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);\nCái này in gì?", "options": [{"icon": "", "label": "60 [1, 2, 3, 4, 5] — filter, map và reduce mỗi cái trả về một mảng hoặc giá trị mới mà không đột biến mảng gốc, vì vậy nums không thay đổi trong khi result là tổng giảm cuối cùng (2 và 4, nhân đôi thành 20 và 40, cộng thành 60)"}, {"icon": "", "label": "60 [2, 4, 20, 40]"}, {"icon": "", "label": "150 [1, 2, 3, 4, 5]"}, {"icon": "", "label": "60 [20, 40]"}]}
      ],
      "optionOrderVersion": "50fc0eba818f2d72"
    }
  }
}
