Node.js — это JavaScript вне браузера. Тот же язык, тот же движок V8,
что в Chrome, только вместо window и document у программы есть файлы,
сеть, переменные окружения и процессы. На Node пишут серверы, API, ботов,
консольные утилиты — и почти все инструменты фронтенда: сборщики,
линтеры, тестовые раннеры.
Программа на Node — это файл, который запускают командой:
node app.jsМодули: один файл — один модуль
Настоящий проект — это десятки файлов. Каждый файл в Node — модуль: у него своя область видимости. Переменные и функции одного файла другим не видны, пока модуль сам их не экспортирует.
Классический способ — CommonJS: модуль кладёт то, чем делится, в
module.exports, а другой файл получает это через require:
// math.js
function sum(a, b) {
return a + b
}
function round2(x) {
return Math.round(x * 100) / 100
}
module.exports = { sum, round2 }// app.js
const { sum } = require('./math')
console.log(sum(2, 3)) // 5require возвращает ровно то, что модуль положил в module.exports, —
обычно объект с функциями. Всё, что не экспортировано, остаётся внутри:
это приватное у модуля.
Встроенные модули
Вместе с Node приходит набор готовых модулей: fs для файлов, path для
путей, http для сервера, events, os, crypto и другие. Их подключают
по имени, без ./:
const fs = require('fs')
const path = require('node:path') // приставка node: — то же самое, но явноПриставка node: подчёркивает, что модуль встроенный, а не пакет из npm с
таким же именем. В новом коде её пишут всё чаще.
require выполняет модуль один раз
При первом require('./math') Node выполняет файл и запоминает его
module.exports. Все следующие require того же файла получают тот же
самый объект — модуль не выполняется заново. Поэтому переменная на
верхнем уровне модуля — общее состояние для всех, кто его подключил:
// counter.js
let count = 0
module.exports = {
next: () => ++count,
}Сколько бы файлов ни подключили counter.js, счётчик у них один.
Ловушка: exports и module.exports
Внутри модуля есть ещё переменная exports — это короткая ссылка на тот же
объект, что и module.exports. Дописывать в неё можно:
exports.sum = sum // работает: дописали свойство в module.exportsА заменять — нельзя:
exports = { sum } // не работает: exports теперь смотрит на новый объект,
// а require по-прежнему вернёт старый — пустойНаружу уходит всегда module.exports. Если хотите отдать объект целиком —
присваивайте именно ему.
А как же import?
В современном Node есть и ES-модули — те же import и export, что в
браузере:
import { readFile } from 'node:fs/promises'
export function sum(a, b) { return a + b }Чтобы их включить, в package.json пишут "type": "module" (или называют
файлы .mjs). В новых проектах так делают всё чаще, но CommonJS вы будете
встречать в каждом втором проекте и пакете — поэтому в курсе мы пишем
require и module.exports.
Как устроены задания этого курса
Каждое задание — один файл, а Node работает прямо в браузере. Встроенные
модули подключаются через require, как обычно, но:
- файлы ненастоящие: диск живёт в памяти, и для каждой проверки тесты
раскладывают на нём нужные файлы. Рабочая папка —
/app; - сети нет: HTTP-сервер создаётся как обычно, а запросы к нему присылают тесты;
- время виртуальное:
setTimeoutна минуту проходит мгновенно.
Пакетов из npm нет — только встроенные модули. Так даже лучше: вы увидите, что умеет сам Node, без фреймворков.