Что происходит при передаче JavaScript-кода в Node.js?

«Что происходит при передаче JavaScript-кода в Node.js?» — вопрос из категории Node.js Core, который задают на 26% собеседований Node.js Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Когда вы выполняете файл (например, node app.js), Node.js проходит через несколько ключевых этапов:

  1. Загрузка и парсинг. Движок V8 загружает исходный код и преобразует его в Абстрактное Синтаксическое Дерево (AST). На этом этапе проверяется базовый синтаксис.

  2. Компиляция. Современный V8 использует JIT-компиляцию (Just-In-Time).

    • Сначала код компилируется в быстрый, но неоптимизированный байт-код (Ignition).
    • "Горячие" участки кода (выполняемые много раз) попадают в оптимизирующий компилятор (TurboFan), который преобразует их в высокооптимизированный машинный код.
  3. Выполнение. Скомпилированный код выполняется в контексте Event Loop. Node.js добавляет в глобальную область видимости свои API-модули (например, require, process, Buffer).

  4. Обработка модулей (CommonJS). При встрече require('./module'):

    • Node.js синхронно читает и выполняет целевой файл.
    • Код модуля оборачивается в функцию, которой передаются переменные exports, require, module, __filename, __dirname.
    • Результат (объект module.exports) кэшируется.

Пример для наглядности:

// app.js
const helper = require('./helper'); // Node.js загружает, парсит, выполняет helper.js и кэширует экспорт
console.log(helper.square(5)); // V8 выполняет скомпилированный код функции square
// helper.js
module.exports.square = (x) => x * x; // Эта функция будет скомпилирована V8

Важный нюанс для Node.js: Весь ваш исходный код выполняется в одном потоке (основном потоке Event Loop). Для CPU-интенсивных задач, которые могут блокировать этот поток, следует использовать Worker Threads или выносить логику в отдельные процессы.