Menu

Node.js runtime: как работает JavaScript вне браузера

Разбираемся, что такое Node.js runtime, чем он отличается от браузера и какие встроенные API - globals, модули, process, fs - делают серверный JavaScript возможным.

На этой странице есть исполняемые редакторы: меняйте, запускайте и сразу видите результат.

Что такое Node.js на самом деле

Node.js - это программа, которую вы ставите на свой компьютер, и она запускает JavaScript-файлы. Проще не скажешь, и это правда. Когда вы пишете в терминале node script.js, Node читает файл, передаёт его движку V8 от Google (тот самый JavaScript-движок из Chrome) и исполняет - плюс сверху навешана огромная библиотека API для всего того, что V8 сам по себе не умеет.

V8 умеет выполнять JavaScript. Но он понятия не имеет, как открыть файл, слушать TCP-сокет, запустить дочерний процесс или прочитать переменную окружения. Всё это даёт Node: написано на C++ и доступно из вашего JavaScript в виде встроенных модулей.

node --version
node script.js

Итак, Node - это не язык. И не фреймворк. Это runtime: движок V8 плюс стандартная библиотека, система модулей и event loop. Вот и всё, собственно.

Первый скрипт

Любой .js-файл - это уже готовая программа для Node. Никакого бойлерплейта, никакой функции main:

console.log в Node работает точно так же, как в браузере. Шаблонные строки, Date, массивы, промисы - все возможности самого языка достаются нам от V8 и ведут себя одинаково. В Node меняется не сам язык, а то, что доступно вокруг него.

Глобальные объекты, которых нет в браузере

В браузере есть window, document, localStorage, fetch. У Node свой набор глобальных объектов - заточенный под серверный рантайм:

  • process - это сам запущенный процесс Node. Через него вы получаете переменные окружения (process.env), аргументы командной строки (process.argv) и методы завершения работы (process.exit(1)).
  • __filename и __dirname возвращают абсолютный путь к текущему файлу и его папке соответственно. (В ES-модулях их нет - вместо них используется import.meta.url.)
  • global - это глобальный объект верхнего уровня, аналог window из браузера.

В Node нет ни document, ни window. Попытка обратиться к ним выбросит ReferenceError. Обычно это первый признак того, что библиотека писалась под браузер и в Node просто так не заведётся.

Аргументы командной строки и переменные окружения

Огромная часть задач, ради которых вообще берут Node, - CLI-утилиты, сборочные скрипты, серверы - так или иначе упирается в чтение аргументов и переменных окружения. И то и другое живёт на объекте process:

process.argv - это массив, где первые два элемента содержат путь к бинарнику Node и путь к запускаемому скрипту, так что реальные аргументы начинаются с индекса 2. process.env - обычный объект с переменными окружения; читать из него NODE_ENV, PORT или API-ключи - общепринятая практика.

Встроенные модули Node.js

В Node из коробки идёт стандартная библиотека, к которой обращаются через require (CommonJS) либо import (ESM). Имена модулей начинаются с префикса node: - так сразу видно, что это встроенные модули:

Чаще всего вам понадобятся:

  • node:fs - чтение и запись файлов. Для варианта с async/await берите node:fs/promises.
  • node:path - склейка, резолвинг и разбор путей к файлам так, чтобы код работал на любой ОС.
  • node:http / node:https - HTTP-серверы и отправка запросов.
  • node:url - разбор и сборка URL.
  • node:os - информация о машине, на которой запущен код.
  • node:crypto - хеширование, случайные байты, шифрование.

Ставить их отдельно не нужно - это встроенные модули Node js, они идут из коробки. Всё остальное тянется из npm.

Event loop в Node js, если коротко

Node выполняет ваш JavaScript в одном потоке, но при этом успевает заниматься кучей дел параллельно. Секрет - в event loop. Когда вы вызываете что-то асинхронное - чтение файла, HTTP-запрос, таймер - Node передаёт саму работу операционной системе (или своему пулу потоков) и спокойно идёт дальше по коду. Как только работа завершается, в очередь попадает колбэк, и цикл подхватывает его, едва текущий код досчитает своё.

Порядок вывода будет такой: 1, 4, 2 (микрозадача), 3 (после 0 мс). Сначала отрабатывает синхронный код. Затем микрозадачи (resolved-промисы). И только потом - таймеры. Именно поэтому тяжёлый CPU-bound цикл блокирует сервер целиком: для вашего кода поток всего один. Параллелизм в Node крутится вокруг I/O, а не вычислений.

Простой HTTP-сервер на Node.js

И вот ради чего всё это затевалось: рабочий веб-сервер умещается в несколько строк:

Без фреймворков и зависимостей. createServer принимает функцию, которая вызывается на каждый запрос, а listen запускает event loop для обработки входящих соединений. В реальных проектах поверх этого обычно лежит Express или Fastify, но под капотом - всё тот же встроенный модуль http.

Node.js против браузера

Стоит чётко разобраться, что работает и там и там, а что - только в одной среде:

Работает вездеТолько Node.jsТолько в браузере
Возможности языка (классы, промисы, async/await)fs, http, process, __dirnamewindow, document, DOM
console.logCommonJS require и особенности ESM в NodelocalStorage, sessionStorage
fetch (начиная с Node 18)Доступ к файловой системе и сетевым сокетамПользовательские события, отрисовка
setTimeout, setIntervalДочерние процессы, потоки (streams)History API, navigator

Современный Node подтянул к себе ряд браузерных API - fetch, URL, AbortController, structuredClone - так что разрыв стал меньше, чем раньше. Но DOM в Node не приедет, а файловая система в браузере не появится.

Node vs Deno vs Bun

Node - выбор по умолчанию, но давно не единственный JavaScript-рантайм. Есть альтернативы: Deno - от автора самого Node, и Bun - от более молодой команды, которая делает ставку на скорость. Оба запускают JavaScript (и TypeScript - прямо из коробки, без настройки), идут вместе со встроенными инструментами вроде тест-раннера и бандлера, и отличаются от Node подходом к модулям, правам доступа и установке пакетов.

Но если вы учите JavaScript, то вся документация, туториалы и вакансии пока крутятся вокруг Node. А ключевые концепции - event loop, модули, встроенные API - переносятся на другие рантаймы практически без изменений. Так что сначала Node, а остальное - когда конкретный проект этого потребует.

Как быстро запускать скрипты

Несколько способов действительно запустить код во время работы:

# Запуск файла
node script.js

# Запуск однострочника
node -e "console.log(2 ** 10)"

# Открыть REPL (интерактивную консоль)
node

# Отслеживать файл и перезапускать при сохранении (Node 18.11+)
node --watch script.js

REPL - удобная песочница, когда нужно быстро проверить, что возвращает какой-нибудь метод, и лень создавать файл. Флаг --watch пригодится в процессе разработки: сохранил - и Node сам перезапустил скрипт.

Дальше: ловим ошибки

Запустить код - это половина дела. Вторая - разобраться, что делать, когда всё пошло не так. Чтение файла падает, HTTP-запрос уходит в таймаут, JSON.parse кидает исключение. В следующей главе как раз поговорим про try/catch, типы ошибок и подходы к обработке того, что ломается, - а в Node-программе рано или поздно ломается всё.

Часто задаваемые вопросы

Что такое Node.js runtime?

Node.js - это среда, которая запускает JavaScript вне браузера. Внутри - движок V8 от Google (тот самый, что крутит JS в Chrome) плюс слой на C++, добавляющий то, чего у V8 самого по себе нет: работу с файловой системой, сетью, процессами, таймерами. Именно эта связка и позволяет писать на JavaScript серверы, CLI-утилиты и инструменты для сборки.

Чем Node отличается от браузера?

Язык один и тот же, но API вокруг него разные. В браузере есть window, document и DOM. В Node - process, fs, http, __dirname и загрузка модулей через CommonJS или ESM. DOM в Node нет, а файловой системы нет в браузере - язык общий, а платформы совершенно разные.

Node.js - это фреймворк или runtime?

Это runtime. Сам Node не навязывает никакой структуры приложения - он просто исполняет JavaScript и предоставляет API. А вот Express, Next.js или NestJS - это уже фреймворки поверх Node. Deno и Bun - альтернативные JS-рантаймы, которые конкурируют с Node, но решают ту же задачу.

Что такое event loop в Node?

Event loop - это механизм, благодаря которому Node успевает делать кучу дел одновременно, работая в одном потоке. Когда вы вызываете что-то асинхронное - чтение файла, HTTP-запрос, setTimeout - Node передаёт работу системе и продолжает выполнять код дальше. Как только работа завершилась, её колбэк попадает в очередь, а event loop его оттуда достаёт. Именно поэтому fs.readFile не блокирует всю остальную программу.

Coddy programming languages illustration

Учитесь программировать с Coddy

НАЧАТЬ