Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Что такое CommonJS-модуль?
CommonJS — это спецификация для модульной системы JavaScript, которая позволяет разбивать код на отдельные файлы (модули) и переиспользовать их в разных частях приложения. CommonJS — это стандарт для Node.js и был основной системой модулей до появления ES6 модулей.
История и контекст
До CommonJS, JavaScript не имел встроенной системы модулей. Все скрипты загружались глобально в браузер, что приводило к:
- Загрязнению глобального scope (все переменные видны везде)
- Конфликтам имен (если два скрипта объявляют переменную с одинаковым именем)
- Сложности управления зависимостями
CommonJS решил эту проблему для серверного JavaScript (Node.js).
Основные концепции
1. Экспорт модуля (module.exports)
Модуль может экспортировать (делиться) свой код с другими модулями через module.exports:
// math.js
function add(a, b) {
return a + b;
}
function subtract(a, b) {
return a - b;
}
// Экспортируем объект с функциями
module.exports = {
add,
subtract
};
Или простой экспорт:
// logger.js
module.exports = function log(message) {
console.log(`[LOG] ${message}`);
};
2. Импорт модуля (require)
Другие файлы могут импортировать модуль через функцию require():
// main.js
const math = require("./math");
const log = require("./logger");
console.log(math.add(5, 3)); // 8
console.log(math.subtract(10, 4)); // 6
log("Application started"); // [LOG] Application started
3. Структура модуля
Каждый CommonJS модуль имеет доступ к:
// module.js
console.log(module); // Объект текущего модуля
console.log(module.id); // ID модуля (обычно путь к файлу)
console.log(module.exports); // То, что экспортируется
console.log(exports); // Ссылка на module.exports
console.log(__filename); // Полный путь к файлу
console.log(__dirname); // Папка файла
console.log(require); // Функция для импорта
Практические примеры
Пример 1: Простая функция
// utils/string.js
module.exports = {
toUpperCase: (str) => str.toUpperCase(),
toLowerCase: (str) => str.toLowerCase(),
capitalize: (str) => str.charAt(0).toUpperCase() + str.slice(1)
};
// app.js
const string = require("./utils/string");
console.log(string.capitalize("hello")); // Hello
Пример 2: Класс или конструктор
// models/User.js
class User {
constructor(name, email) {
this.name = name;
this.email = email;
}
getInfo() {
return `${this.name} (${this.email})`;
}
}
module.exports = User;
// app.js
const User = require("./models/User");
const user = new User("John", "john@example.com");
console.log(user.getInfo()); // John (john@example.com)
Пример 3: Конфигурация
// config.js
module.exports = {
app: {
port: 3000,
host: "localhost"
},
database: {
url: "postgresql://localhost/mydb",
pool: { max: 10 }
},
jwt: {
secret: process.env.JWT_SECRET || "your-secret-key",
expires: "7d"
}
};
// server.js
const config = require("./config");
const express = require("express");
const app = express();
app.listen(config.app.port, config.app.host);
Пример 4: Middleware в Express
// middleware/auth.js
module.exports = (req, res, next) => {
const token = req.headers.authorization?.split(" ")[1];
if (!token) {
return res.status(401).json({ error: "No token" });
}
req.user = verifyToken(token);
next();
};
// server.js
const express = require("express");
const auth = require("./middleware/auth");
const app = express();
app.use(auth); // Использование middleware
app.get("/profile", (req, res) => {
res.json(req.user);
});
Важные различия между exports и module.exports
// ⚠️ Важно понимать разницу
// Способ 1: Добавление к module.exports
module.exports.add = (a, b) => a + b;
module.exports.subtract = (a, b) => a - b;
// Способ 2: Переопределение module.exports
module.exports = {
add: (a, b) => a + b,
subtract: (a, b) => a - b
};
// ❌ ОПАСНО: exports перестанет работать
exports.multiply = (a, b) => a * b;
module.exports = { divide: (a, b) => a / b };
// multiply не будет экспортирован!
Кэширование модулей
Важная особенность: Node.js кэширует модули. При первом require() модуль загружается и выполняется, затем результат кэшируется. Последующие require() возвращают кэшированный объект:
// counter.js
let count = 0;
module.exports = {
increment: () => ++count,
getCount: () => count
};
// test.js
const counter1 = require("./counter");
const counter2 = require("./counter");
counter1.increment();
console.log(counter1.getCount()); // 1
console.log(counter2.getCount()); // 1 (ОДИН И ТОТ ЖЕ ОБЪЕКТ!)
console.log(counter1 === counter2); // true
Это означает, что состояние модуля сохраняется между импортами.
Пути к модулям
// Относительный путь
const utils = require("./utils"); // ./utils.js или ./utils/index.js
const config = require("../config"); // выше на один уровень
// Абсолютный путь
const math = require("/home/user/app/math");
// NPM пакеты (node_modules)
const express = require("express"); // поиск в node_modules
const _ = require("lodash"); // внешний пакет
// Явное расширение
const myModule = require("./myModule.js");
Сравнение с ES6 модулями
| Аспект | CommonJS | ES6 Module |
|---|---|---|
| Экспорт | module.exports | export / export default |
| Импорт | require() | import |
| Синхронность | Синхронный | Асинхронный |
| Кэширование | Да | Да |
| Браузер | Нет | Да |
| Node.js | Нативно | Нужен флаг --experimental-modules |
Современное использование
Сегодня в Node.js:
- CommonJS — по умолчанию в
.jsфайлах - ES6 modules — в
.mjsфайлах или если"type": "module"вpackage.json
{
"name": "my-app",
"version": "1.0.0",
"type": "module"
}
// Теперь можно использовать ES6 синтаксис
import express from "express";
import { add, subtract } from "./math.js";
export function myFunction() { ... }
Итоги
CommonJS модуль — это:
- Самостоятельный файл с кодом
- Экспортирует данные через
module.exports - Импортирует другие модули через
require() - Кэшируется Node.js
- Синхронный (в отличие от ES6 модулей)
- Стандарт для Node.js последние 15+ лет
CommonJS остается важной частью экосистемы Node.js и должен быть хорошо понят каждым backend разработчиком.