Что такое 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, __dirname | window, document, DOM |
console.log | CommonJS require и особенности ESM в Node | localStorage, 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 не блокирует всю остальную программу.