{
  "assessmentTests": {
    "javascript_test": {
      "name": "Тест по JavaScript",
      "desc": "30 сценарных вопросов по основному синтаксису и приведению типов, функциям/области видимости/замыканиям и this, объектам/массивам и структурам данных, а также event loop/async/прототипным идиомам — узнайте, соответствует ли ваш JavaScript тому, что работодатель имеет в виду под профессиональным уровнем JavaScript.",
      "recommendation": "Ваш профиль навыков JavaScript",
      "results": {
        "beginner": {
          "name": "Начинающий",
          "desc": "Вы можете писать рабочие функции и объекты, но вопросы, на которых вы ошиблись, сосредоточены вокруг приведения типов и семантики ссылок, а не синтаксиса — == тихо преобразует типы перед сравнением, присваивание массива создаёт общую ссылку вместо копии, почему NaN никогда не равен самому себе. Это не о плохом знании JavaScript; это специфические правила, которые запутывают людей, выучивших язык, написав код, который просто работал, вместо изучения того, как значения и ссылки на самом деле ведут себя. Они важны на работе, потому что каждое из них — это место, где код работает без ошибок и всё равно делает неправильное.",
          "recommendation": "Начните с трёх вещей, в таком порядке: почему == приводит типы перед сравнением, а === этого не делает; почему присваивание одного массива или объекта новой переменной копирует ссылку, а не значение; как var поднимается и инициализируется как undefined, а let и const остаются в мёртвой зоне до тех пор, пока их объявление не выполнится. Руководство MDN по JavaScript охватывает все три с выполняемыми примерами."
        },
        "intermediate": {
          "name": "Средний уровень",
          "desc": "Вы удобно работаете с повседневным кодом приложений — функциями, методами массивов, простым управлением потоком — и не будете замедлены рутинной разработкой функций. Пропасть между здесь и Advanced в основном в том, как правила области видимости и синхронизации JavaScript взаимодействуют: var, захваченный внутри callbacks цикла, который все читают как одно и то же финальное значение; this, который молча перестаёт указывать на объект, когда метод отделяется от него; spread-копирование, которое выглядит глубоким, но копирует только один уровень. Это такие ошибки, которые проходят быстрое чтение и появляются только под специфическим условием времени выполнения.",
          "recommendation": "Сосредоточьтесь на том, как область видимости и this разрешаются в момент вызова, а не в момент определения: почему for-цикл с var передаёт каждому callback одно и то же финальное значение счётчика, а let — нет; как this определяется тем, как функция вызывается, а не где она написана; почему spread или Object.assign копируют только верхний уровень объекта. Потом event loop, так как это то, что запутывает людей, которые уже понимают promises по отдельности."
        },
        "advanced": {
          "name": "Продвинутый",
          "desc": "Это уровень, который большинство объявлений о вакансиях имеют в виду под «сильный JavaScript». Вы читаете цепочку filter/map/reduce и можете предсказать точно, что она вернёт, без изменения исходного массива; вы знаете, почему синхронный console.log всегда выигрывает у microtask, который всегда выигрывает у setTimeout; вы достаёте Promise.all, зная, что он отклоняется в момент, когда любой promise отклоняется, а не ждёт остальных. Что отделяет эту полосу от верхней — это прототипная и объектная модель работы: как цепочка прототипов разрешает наследованный метод в сравнении с hasOwnProperty; что приватное поле класса на самом деле скрывает.",
          "recommendation": "Продвигайтесь в части, которые защищают состояние, к которому также может получить доступ другой код: как цепочка прототипов JavaScript разрешает поиск методов, которых нет в самом экземпляре; что приватные #fields класса на самом деле скрывают от внешнего доступа; как JSON.stringify обрабатывает undefined и функции по-разному внутри обычного объекта в сравнении с массивом. Материал MDN о event loop и цепочке прототипов — естественный следующий шаг для обоих."
        },
        "expert": {
          "name": "Эксперт",
          "desc": "Вы набрали максимальный балл в каждой секции — основной синтаксис, типы и приведение; функции, область видимости, замыкания и this; объекты, массивы и структуры данных; и async, event loop, прототипы и идиомы. На практике это означает, что вы можете взять чужую функцию и объяснить, почему она возвращает то, что возвращает, а не просто то, что синтаксис говорит, что она должна делать — это более сложный и более ценный навык. На этом уровне сам язык редко является ограничивающим фактором; лимит обычно в асинхронном управлении потоком или в форме данных под ним.",
          "recommendation": "Отдача сейчас в дизайне и диагностике: рассуждение о race condition между Promise.all и Promise.race, а не просто их определения; выбор, когда замыкание — это правильный инструмент для приватного состояния, а когда приватное поле класса; чтение ошибки microtask-vs-macrotask в логах production, а не в викторине. Если вас проверяют на роль, опишите ошибку синхронизации, которую вы нашли — застарелую переменную замыкания или неправильный асинхронный callback — вместо того, чтобы называть особенности JavaScript; это демонстрирует рассуждение, а не просто словарь."
        }
      },
      "questions": [
        {
          "question": "console.log(x);\nvar x = 5;\nЧто будет выведено?",
          "options": [
            {
              "icon": "",
              "label": "5 — объявления var выполняются сразу же, до любого другого кода"
            },
            {
              "icon": "",
              "label": "ReferenceError: x is not defined"
            },
            {
              "icon": "",
              "label": "undefined — объявления var поднимаются в начало своей области видимости и инициализируются значением undefined, но присваивание выполняется на месте, поэтому до его выполнения переменная существует, но содержит undefined"
            },
            {
              "icon": "",
              "label": "null"
            }
          ]
        },
        {
          "question": "console.log(y);\nlet y = 5;\nЧто произойдёт?",
          "options": [
            {
              "icon": "",
              "label": "Будет выброшено исключение ReferenceError: Cannot access 'y' before initialization — let поднимается в начало своей области видимости, но остаётся в мёртвой зоне до тех пор, пока строка объявления не выполнится"
            },
            {
              "icon": "",
              "label": "Будет выведено undefined, как и для var"
            },
            {
              "icon": "",
              "label": "Будет выведено 5"
            },
            {
              "icon": "",
              "label": "Будет выброшено исключение SyntaxError во время разбора кода"
            }
          ]
        },
        {
          "question": "const arr = [1, 2, 3];\narr.push(4);\nconsole.log(arr.length);\nЧто будет выведено?",
          "options": [
            {
              "icon": "",
              "label": "4 — const только предотвращает переприсваивание самой привязки arr, но не мутирование объекта массива, на который она указывает, поэтому push всё ещё работает"
            },
            {
              "icon": "",
              "label": "Будет выброшено исключение TypeError, потому что arr является const"
            },
            {
              "icon": "",
              "label": "3 — push заблокирован для const массива"
            },
            {
              "icon": "",
              "label": "undefined"
            }
          ]
        },
        {
          "question": "console.log('' == 0);\nconsole.log('' === 0);\nЧто будут выводить эти две строки?",
          "options": [
            {
              "icon": "",
              "label": "true, потом true"
            },
            {
              "icon": "",
              "label": "false, потом false"
            },
            {
              "icon": "",
              "label": "true, потом false — == преобразует пустую строку в 0 перед сравнением, а === сравнивает тип и значение без преобразования, и строка никогда не будет строго равна числу"
            },
            {
              "icon": "",
              "label": "false, потом true"
            }
          ]
        },
        {
          "question": "console.log(NaN === NaN);\nconsole.log(Number.isNaN(NaN));\nЧто будут выводить эти две строки?",
          "options": [
            {
              "icon": "",
              "label": "true, потом true"
            },
            {
              "icon": "",
              "label": "true, потом false"
            },
            {
              "icon": "",
              "label": "false, потом false"
            },
            {
              "icon": "",
              "label": "false, потом true — NaN — единственное значение в JavaScript, которое не равно самому себе при ===, но Number.isNaN() всё равно правильно его обнаруживает"
            }
          ]
        },
        {
          "question": "console.log(typeof null);\nЧто будет выведено?",
          "options": [
            {
              "icon": "",
              "label": "\"null\""
            },
            {
              "icon": "",
              "label": "\"object\" — давняя ошибка в языке: typeof null возвращает \"object\", хотя null является примитивом, и это было сохранено для обратной совместимости"
            },
            {
              "icon": "",
              "label": "\"undefined\""
            },
            {
              "icon": "",
              "label": "\"boolean\""
            }
          ]
        },
        {
          "question": "Какое из следующих значений является истинным (truthy) в условии if?",
          "options": [
            {
              "icon": "",
              "label": "0"
            },
            {
              "icon": "",
              "label": "\"\" (пустая строка)"
            },
            {
              "icon": "",
              "label": "null"
            },
            {
              "icon": "",
              "label": "\"0\" — непустая строка всегда является истинной (truthy) в JavaScript, даже если её содержимое само по себе является строкой \"0\""
            }
          ]
        },
        {
          "question": "foo();\nfunction foo() { console.log('a'); }\nbar();\nvar bar = function() { console.log('b'); };\nЧто произойдёт?",
          "options": [
            {
              "icon": "",
              "label": "Обе строки выведут результат: \"a\", потом \"b\""
            },
            {
              "icon": "",
              "label": "foo() выведет \"a\"; bar() выбросит исключение TypeError: bar is not a function — объявления функций полностью поднимаются вместе с телом, но переменная с присвоением функции поднимается только как undefined, без самого присваивания"
            },
            {
              "icon": "",
              "label": "Обе строки выбросят исключение ReferenceError"
            },
            {
              "icon": "",
              "label": "foo() выбросит исключение; bar() выведет \"b\""
            }
          ]
        },
        {
          "question": "function makeCounter() {\n  let count = 0;\n  return function() { count++; return count; };\n}\nconst counter = makeCounter();\nconsole.log(counter());\nconsole.log(counter());\nЧто будут выводить эти две строки?",
          "options": [
            {
              "icon": "",
              "label": "1, потом 1"
            },
            {
              "icon": "",
              "label": "0, потом 1"
            },
            {
              "icon": "",
              "label": "undefined, потом undefined"
            },
            {
              "icon": "",
              "label": "1, потом 2 — возвращённая функция замыкается над той же переменной count из области видимости makeCounter, и эта переменная сохраняется между вызовами вместо перезагрузки"
            }
          ]
        },
        {
          "question": "for (var i = 0; i < 3; i++) {\n  setTimeout(() => console.log(i), 0);\n}\nЧто будет выведено?",
          "options": [
            {
              "icon": "",
              "label": "0, 1, 2"
            },
            {
              "icon": "",
              "label": "3, 3, 3 — var имеет область видимости функции, а не блока, поэтому все три колбэка замыкаются над одной и той же переменной i, которая уже закончила цикл и равна 3 к тому времени, когда любой из них выполнится"
            },
            {
              "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}\nЧто будет напечатано?",
          "options": [
            {
              "icon": "",
              "label": "3, 3, 3"
            },
            {
              "icon": "",
              "label": "0, 0, 0"
            },
            {
              "icon": "",
              "label": "undefined, undefined, undefined"
            },
            {
              "icon": "",
              "label": "0, 1, 2 — let создаёт новую привязку переменной i для каждой итерации цикла, поэтому каждый callback закрывает свою отдельную копию, а не общую переменную"
            }
          ]
        },
        {
          "question": "function greet() { return `Hi, ${this.name}`; }\nconst person = { name: 'Ana' };\nconsole.log(greet.call(person));\nЧто будет напечатано?",
          "options": [
            {
              "icon": "",
              "label": "\"Hi, Ana\" — Function.prototype.call вызывает функцию с явным установлением this на первый аргумент, поэтому this.name разрешается в контексте person"
            },
            {
              "icon": "",
              "label": "\"Hi, undefined\" — call() работает только с функциями, которые уже являются методами объекта"
            },
            {
              "icon": "",
              "label": "Будет выброшена ошибка TypeError, потому что greet не является методом объекта person"
            },
            {
              "icon": "",
              "label": "\"Hi, ${this.name}\" — call() не вычисляет строку-шаблон"
            }
          ]
        },
        {
          "question": "const timer = {\n  seconds: 0,\n  start: function() {\n    setInterval(() => { this.seconds++; }, 1000);\n  },\n};\nПочему люди предпочитают стрелочную функцию обычной function() { this.seconds++ } внутри setInterval в этом случае?",
          "options": [
            {
              "icon": "",
              "label": "Стрелочные функции не имеют собственного this — они захватывают this лексически из окружающей функции start, которая является timer (так как start была вызвана как timer.start()), поэтому this.seconds корректно ссылается на timer.seconds на каждом тике"
            },
            {
              "icon": "",
              "label": "Стрелочные функции выполняются быстрее, чем обычные функции во время выполнения"
            },
            {
              "icon": "",
              "label": "Стрелочные функции автоматически привязывают this к глобальному объекту, который совпадает с timer"
            },
            {
              "icon": "",
              "label": "Стрелочные функции автоматически преобразуют this в глобальную переменную"
            }
          ]
        },
        {
          "question": "function multiply(a, b) { return a * b; }\nconst double = multiply.bind(null, 2);\nconsole.log(double(5));\nЧто будет напечатано?",
          "options": [
            {
              "icon": "",
              "label": "Будет выброшена ошибка, потому что bind требует оба аргумента сразу"
            },
            {
              "icon": "",
              "label": "10 — bind возвращает новую функцию с this и предустановленными аргументами; вызов double(5) поставляет оставшийся аргумент b=5, поэтому выполняется multiply(2, 5)"
            },
            {
              "icon": "",
              "label": "NaN"
            },
            {
              "icon": "",
              "label": "7, потому что bind добавляет свои аргументы к вызову, а не заменяет их"
            }
          ]
        },
        {
          "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();\nЧто напечатают эти два console.log вызова?",
          "options": [
            {
              "icon": "",
              "label": "20 then 20"
            },
            {
              "icon": "",
              "label": "20 then 10 — переменная let x во inner затеняет x из outer только внутри области видимости inner; собственная x в outer никогда не изменяется"
            },
            {
              "icon": "",
              "label": "10 then 10"
            },
            {
              "icon": "",
              "label": "Будет выброшена ошибка ReferenceError, потому что x объявлена дважды"
            }
          ]
        },
        {
          "question": "const a = [1, 2, 3];\nconst b = a;\nb.push(4);\nconsole.log(a.length);\nЧто будет напечатано?",
          "options": [
            {
              "icon": "",
              "label": "3 — b это отдельная копия a"
            },
            {
              "icon": "",
              "label": "undefined"
            },
            {
              "icon": "",
              "label": "Будет выброшена ошибка TypeError, потому что a объявлена с const"
            },
            {
              "icon": "",
              "label": "4 — массивы это объекты, поэтому const b = a копирует ссылку, а не сам массив; изменение b через push также изменяет тот же самый массив, на который указывает 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);\nЧто будет напечатано?",
          "options": [
            {
              "icon": "",
              "label": "'Ana' 2 — spread глубоко клонирует каждый вложенный объект и массив"
            },
            {
              "icon": "",
              "label": "'Ana' 3 — spread это только поверхностная копия: примитивное свойство, как name, копируется по значению, поэтому переприсваивание copy.name никогда не трогает original, а вложенный массив, как tags, копируется по ссылке, поэтому добавление в copy.tags изменяет тот же самый массив, на который указывает original.tags"
            },
            {
              "icon": "",
              "label": "'Bea' 3 — spread копирует всё по ссылке, поэтому и изменения на верхнем уровне, и вложенные изменения отражаются на original"
            },
            {
              "icon": "",
              "label": "'Bea' 2"
            }
          ]
        },
        {
          "question": "const { a: x = 10, b: y = 20 } = { a: 5 };\nconsole.log(x, y);\nЧто будет напечатано?",
          "options": [
            {
              "icon": "",
              "label": "undefined undefined"
            },
            {
              "icon": "",
              "label": "5 undefined"
            },
            {
              "icon": "",
              "label": "10 20"
            },
            {
              "icon": "",
              "label": "5 20 — a переименована в x и присутствует, поэтому сохраняет своё значение 5; b переименована в y и отсутствует, поэтому использует значение по умолчанию 20"
            }
          ]
        },
        {
          "question": "const [first, ...rest] = [1, 2, 3, 4];\nconsole.log(rest);\nЧто будет напечатано?",
          "options": [
            {
              "icon": "",
              "label": "[1, 2, 3, 4]"
            },
            {
              "icon": "",
              "label": "[2, 3, 4] — rest собирает каждый оставшийся элемент после first в совершенно новый массив"
            },
            {
              "icon": "",
              "label": "2"
            },
            {
              "icon": "",
              "label": "[1]"
            }
          ]
        },
        {
          "question": "const nums = [10, 1, 2];\nnums.sort();\nconsole.log(nums);\nЧто будет напечатано?",
          "options": [
            {
              "icon": "",
              "label": "[1, 2, 10] — sort всегда упорядочивает числа численно в возрастающем порядке по умолчанию"
            },
            {
              "icon": "",
              "label": "[10, 1, 2] — sort не изменяет исходный массив"
            },
            {
              "icon": "",
              "label": "[1, 10, 2] — компаратор Array.prototype.sort по умолчанию преобразует элементы в строки и сравнивает их лексикографически, поэтому \"10\" сортируется раньше \"2\"; sort также изменяет массив на месте, а не возвращает новый"
            },
            {
              "icon": "",
              "label": "[2, 1, 10]"
            }
          ]
        },
        {
          "question": "const user = { profile: { age: 0 } };\nconsole.log(user.profile?.age ?? 'unknown');\nconsole.log(user.address?.city ?? 'unknown');\nЧто выведут эти две строки?",
          "options": [
            {
              "icon": "",
              "label": "'unknown', затем 'unknown' — ?? рассматривает 0 как пропущенное значение, так же как это делает ||"
            },
            {
              "icon": "",
              "label": "0, затем 'unknown' — ?? использует резервное значение только при null или undefined, но не при других ложных значениях вроде 0, поэтому age (0) остаётся неизменной; user.address равна undefined, поэтому ?. прерывает вычисление цепи, приводя к undefined, и ?? подставляет 'unknown'"
            },
            {
              "icon": "",
              "label": "0, затем undefined"
            },
            {
              "icon": "",
              "label": "'unknown', затем 0"
            }
          ]
        },
        {
          "question": "const obj = { a: undefined, b: function(){}, c: NaN, d: [undefined, function(){}, 1] };\nconsole.log(JSON.stringify(obj));\nЧто это выведет?",
          "options": [
            {
              "icon": "",
              "label": "'{\"a\":null,\"b\":null,\"c\":null,\"d\":[null,null,1]}' — каждое неподдерживаемое значение становится null везде, в объектах и массивах"
            },
            {
              "icon": "",
              "label": "'{\"a\":undefined,\"b\":undefined,\"c\":NaN,\"d\":[undefined,null,1]}'"
            },
            {
              "icon": "",
              "label": "'{\"c\":null,\"d\":[null,null,1]}' — свойства объекта, значения которых undefined или функция, опускаются полностью, и NaN сериализуется как null, но внутри массива эти же неподдерживаемые значения становятся null вместо того, чтобы быть опущены, потому что удаление элемента массива изменило бы индексы всех последующих элементов"
            },
            {
              "icon": "",
              "label": "Выбросит TypeError, потому что NaN невозможно сериализовать в JSON"
            }
          ]
        },
        {
          "question": "console.log('1');\nsetTimeout(() => console.log('2'), 0);\nPromise.resolve().then(() => console.log('3'));\nconsole.log('4');\nВ каком порядке они выведены?",
          "options": [
            {
              "icon": "",
              "label": "1, 2, 3, 4"
            },
            {
              "icon": "",
              "label": "1, 4, 2, 3"
            },
            {
              "icon": "",
              "label": "1, 4, 3, 2 — синхронный код выполняется первым (поэтому 1 и 4 выводятся сразу), затем очередь микротасков (коллбеки Promise) полностью опустошается, прежде чем цикл событий перейдёт к следующему макротаску, поэтому 3 выводится раньше, чем 2 из коллбека 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');\nВ каком порядке они выведены?",
          "options": [
            {
              "icon": "",
              "label": "start, a, end, b — foo() выполняется синхронно вплоть до первого await, поэтому 'a' выводится сразу при вызове foo(); await затем приостанавливает foo и возвращает управление вызывающему коду, поэтому 'end' выводится раньше, чем возобновленный 'b', который выполняется позже как микротаск"
            },
            {
              "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));\nЧто это выведет?",
          "options": [
            {
              "icon": "",
              "label": "'fail err' — Promise.all отклоняется, как только один из его входных Promise'ов отклонится, игнорируя результаты других, поэтому обработчик catch выполняется с причиной этого отклонения"
            },
            {
              "icon": "",
              "label": "'ok' [1, 3]"
            },
            {
              "icon": "",
              "label": "'ok' [1, undefined, 3]"
            },
            {
              "icon": "",
              "label": "Выбросит ошибку синхронно и ни один обработчик не выполнится"
            }
          ]
        },
        {
          "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));\nЧто это выведет?",
          "options": [
            {
              "icon": "",
              "label": "'slow'"
            },
            {
              "icon": "",
              "label": "['fast', 'slow']"
            },
            {
              "icon": "",
              "label": "'fast' — Promise.race разрешается, как только один из его входных Promise'ов разрешится, и таймер 10ms разрешается намного раньше 100ms"
            },
            {
              "icon": "",
              "label": "Выбросит ошибку, потому что race требует ровно два Promise'а"
            }
          ]
        },
        {
          "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'));\nЧто выведут эти две строки?",
          "options": [
            {
              "icon": "",
              "label": "'Rex makes a sound', затем true"
            },
            {
              "icon": "",
              "label": "undefined, затем false"
            },
            {
              "icon": "",
              "label": "Выбросит TypeError, потому что dog не имеет собственного метода speak"
            },
            {
              "icon": "",
              "label": "'Rex makes a sound', затем false — speak определена на Animal.prototype, а не на самом экземпляре dog; dog находит её через цепочку прототипов при вызове, но hasOwnProperty проверяет только собственные свойства экземпляра, а не что-то унаследованное"
            }
          ]
        },
        {
          "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);\nЧто выведут эти две строки?",
          "options": [
            {
              "icon": "",
              "label": "1, затем undefined — #count это приватное поле класса, доступное для чтения и записи только из методов самого класса; c.count (без #) это совершенно другое обычное свойство, которое никогда не устанавливалось, поэтому оно undefined"
            },
            {
              "icon": "",
              "label": "1, затем 0"
            },
            {
              "icon": "",
              "label": "1, затем 1"
            },
            {
              "icon": "",
              "label": "Выбросит SyntaxError при доступе к c.count"
            }
          ]
        },
        {
          "question": "const items = ['a', 'b'];\nconst label = `Items: ${items.length > 0 ? items.join(', ') : 'none'}`;\nconsole.log(label);\nЧто это выведет?",
          "options": [
            {
              "icon": "",
              "label": "'Items: ${items.length > 0 ? items.join(\\', \\') : \\'none\\'}'"
            },
            {
              "icon": "",
              "label": "'Items: none'"
            },
            {
              "icon": "",
              "label": "'Items: a, b' — шаблонные строки вычисляют любое выражение, помещённое внутри ${...}, включая полный условный оператор, и интерполируют его результат прямо в строку"
            },
            {
              "icon": "",
              "label": "Выбросит SyntaxError, потому что условные операторы не допускаются внутри шаблонных строк"
            }
          ]
        },
        {
          "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);\nЧто это выведет?",
          "options": [
            {
              "icon": "",
              "label": "60 [1, 2, 3, 4, 5] — filter, map и reduce каждый возвращают новый массив или значение без изменения исходного массива, поэтому nums остаётся неизменной, а result это окончательное значение редукции (2 и 4 увеличены в 10 раз, давая 20 и 40, в сумме 60)"
            },
            {
              "icon": "",
              "label": "60 [2, 4, 20, 40]"
            },
            {
              "icon": "",
              "label": "150 [1, 2, 3, 4, 5]"
            },
            {
              "icon": "",
              "label": "60 [20, 40]"
            }
          ]
        }
      ],
      "optionOrderVersion": "50fc0eba818f2d72"
    }
  }
}
