Открыть сервис

CommonJS

CommonJS — это проект по стандартизации экосистемы модулей в среде выполнения JavaScript вне браузера, а также спецификация, определяющая формат и механизмы загрузки модулей. Основной целью CommonJS является создание единого подхода к организации кода, управлению зависимостями и пространствами имён, что особенно актуально для серверных приложений, утилит командной строки и других сред, где отсутствует встроенная модульная система браузера. Наиболее известной реализацией спецификации CommonJS является модульная система платформы Node.js.

История

Предпосылки появления

В начале 2000-х годов язык JavaScript использовался преимущественно в браузерах, где модульность обеспечивалась за счёт глобальных переменных и тегов <script>. С ростом популярности серверного JavaScript, в частности с появлением платформы Node.js в 2009 году, возникла острая необходимость в формальной системе модулей, которая бы позволяла изолировать код, избегать конфликтов имён и управлять зависимостями.

Создание проекта

Проект CommonJS был основан в 2009 году инженером Кевином Дангуром (Kevin Dangoor) под названием «ServerJS». Первоначально он был ориентирован исключительно на серверные среды, но вскоре его цели были расширены на более широкий спектр приложений, и проект был переименован в CommonJS. В рамках проекта были разработаны спецификации для модулей, пакетов, системных утилит и других компонентов.

Влияние на Node.js

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

Дальнейшее развитие

С появлением стандарта ECMAScript 2015 (ES6), который ввёл нативную модульную систему (ES modules), популярность CommonJS начала снижаться. Однако благодаря огромной кодовой базе Node.js и npm, CommonJS остаётся широко используемым форматом, особенно в унаследованных проектах и библиотеках, не перешедших на ES modules.

Ключевые особенности спецификации

Синхронная загрузка

В отличие от асинхронных модульных систем браузера (например, AMD), CommonJS предполагает синхронную загрузку модулей. Это означает, что при вызове require() выполнение программы приостанавливается до полной загрузки и выполнения модуля. Такой подход оправдан в серверных средах, где файлы загружаются с локального диска, а не по сети.

Одноимённое пространство имён

Каждый модуль CommonJS имеет собственную область видимости. Переменные, объявленные внутри модуля, не попадают в глобальную область, что предотвращает конфликты имён. Для экспорта данных используется объект module.exports или его сокращение exports.

Кэширование модулей

После первого вызова require() для конкретного модуля он загружается, выполняется и кэшируется. Последующие вызовы require() для того же модуля возвращают его кэшированный объект module.exports, что повышает производительность и обеспечивает единообразие состояния.

Основные компоненты

require()

Функция require() является основным механизмом импорта модулей. Она принимает идентификатор модуля (путь к файлу или имя пакета) и возвращает объект, экспортированный этим модулем. Пример: ``javascript const fs = require('fs'); const myModule = require('./my-module'); ``

module.exports и exports

Объект module.exports определяет, что будет возвращено при вызове require(). По умолчанию это пустой объект. Разработчик может присвоить ему любое значение: объект, функцию, класс или примитив. Сокращение exports является ссылкой на module.exports, но его нельзя переопределять напрямую, так как это разорвёт связь.

Пример экспорта объекта: ``javascript module.exports = { greet: function(name) { return Hello, ${name}!; } }; ``

Пример экспорта функции: ``javascript module.exports = function(a, b) { return a + b; }; ``

Идентификаторы модулей

Идентификаторы модулей могут быть относительными (начинаются с ./ или ../), абсолютными (начинаются с /) или именами пакетов (без пути). Относительные и абсолютные идентификаторы указывают на конкретные файлы, а имена пакетов ищутся в каталоге node_modules.

Структура пакета

package.json

Файл package.json является центральным элементом пакета CommonJS. Он содержит метаданные: имя, версию, описание, точку входа (поле main), зависимости и другие сведения. Пример минимального package.json: ``json { "name": "my-package", "version": "1.0.0", "main": "index.js" } ``

Каталог node_modules

Каталог node_modules содержит установленные зависимости пакета. Менеджер пакетов npm помещает туда все модули, указанные в dependencies и devDependencies файла package.json. При вызове require('package-name') Node.js ищет модуль в этом каталоге, рекурсивно поднимаясь по дереву каталогов.

Пример использования

Создание простого модуля math.js: ```javascript // math.js const add = (a, b) => a + b; const subtract = (a, b) => a - b;

module.exports = { add, subtract }; ```

Использование модуля в app.js: ``javascript // app.js const math = require('./math'); console.log(math.add(2, 3)); // 5 console.log(math.subtract(5, 2)); // 3 ``

Сравнение с другими модульными системами

CommonJS и AMD

AMD (Asynchronous Module Definition) была разработана для браузеров и поддерживает асинхронную загрузку модулей. CommonJS, напротив, синхронен и ориентирован на серверные среды. AMD использует функцию define() для объявления модулей, в то время как CommonJS использует require() и module.exports.

CommonJS и ES modules

ES modules (ECMAScript 2015) являются нативным стандартом для JavaScript. Они поддерживают статический анализ, асинхронную загрузку и более строгий синтаксис (import/export). CommonJS, будучи динамическим и синхронным, не может быть полностью проанализирован статически, что ограничивает возможности оптимизации (например, tree-shaking). В Node.js обе системы сосуществуют, но их смешивание в одном проекте может приводить к ошибкам.

Критика и ограничения

Синхронность

Синхронная загрузка делает CommonJS непригодным для браузерных сред без дополнительных инструментов сборки (например, Webpack или Browserify), так как загрузка по сети требует асинхронности.

Отсутствие статического анализа

Динамическая природа require() (возможность передавать переменные в качестве аргумента) не позволяет инструментам статического анализа определять зависимости на этапе компиляции, что усложняет оптимизацию кода.

Проблемы с циклическими зависимостями

CommonJS допускает циклические зависимости, но их обработка может приводить к неопределённому поведению, так как модуль может быть загружен не полностью на момент вызова require().

Влияние на экосистему JavaScript

CommonJS сыграл ключевую роль в становлении Node.js как платформы для серверной разработки. Он обеспечил стандартизацию модульности, что позволило создать крупнейшую экосистему пакетов — npm. Многие инструменты сборки, такие как Webpack и Rollup, поддерживают CommonJS для обратной совместимости. Несмотря на переход к ES modules, CommonJS остаётся важной частью истории и современного ландшафта JavaScript.

Источники

  • Kevin Dangoor. «CommonJS: JavaScript Standard Library». 2009.
  • Официальная документация Node.js. «Modules: CommonJS modules».
  • Документация npm. «package.json».
  • Спецификация CommonJS 1.0.

BFOmetr — база данных и аналитика по компаниям России.

На главную BFOmetr →