Мы уже упоминали о нескольких важных различиях между ESM и CommonJS, таких как необходимость явно указывать расширения файлов при импорте с помощью ESM, в то время как расширения файлов являются совершенно необязательными для функции CommonJS require.

Давайте завершим эту статью, обсудив некоторые другие важные различия между ESM и CommonJS и то, как две модульные системы могут работать вместе, когда это необходимо.

ECMAScript РАБОТАЕТ В СТРОГОМ РЕЖИМЕ

Модули ES выполняются неявно в строгом режиме. Это означает, что нам не нужно явно добавлять операторы use strict в начале каждого файла. Строгий режим нельзя отключить; поэтому мы не можем использовать необъявленные переменные или оператор with или иметь другие функции, доступные только в нестрогом режиме, но это, безусловно, хорошо, поскольку строгий режим является более безопасным режимом выполнения.

НЕДОСТАЮЩИЕ ССЫЛКИ В ECMAScript

В ESM некоторые важные ссылки CommonJS не определены. К ним относятся require , exports , module.exports , __filename и __dirname . Если мы попытаемся использовать любой из них в модуле ES, поскольку он также работает в строгом режиме, мы получим ReferenceError :

Мы уже подробно обсуждали значение экспорта и модуля в CommonJS; __filename и __dirname представляют абсолютный путь к текущему файлу модуля и абсолютный путь к его родительской папке. Эти специальные переменные могут быть очень полезны, когда нам нужно построить путь относительно текущего файла.

В ESM можно получить ссылку на URL текущего файла с помощью специального объекта import.meta . В частности, import.meta.url — это ссылка на файл текущего модуля в формате, подобном file:///path/to/current_module.js. Это значение можно использовать для восстановления __filename и __dirname в виде абсолютных путей:

import { fileURLToPath } from 'url'
import { dirname } from 'path'
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)

Также можно воссоздать функцию require() следующим образом:

import { createRequire } from 'module'
const require = createRequire(import.meta.url)

Теперь мы можем использовать require() для импорта функций из модулей CommonJS в контексте модулей ES.

Еще одним интересным отличием является поведение ключевого слова this.

В глобальной области модуля ES это не определено, а в CommonJS это является ссылкой на экспорты:

// this.js - ESM
console.log(this) // undefined
// this.cjs – CommonJS
console.log(this === exports) // true

ВЗАИМОДЕЙСТВИЕ

В предыдущем разделе мы обсуждали, как импортировать модули CommonJS в ESM с помощью функции module.createRequire. Также можно импортировать модули CommonJS из ESM, используя стандартный синтаксис импорта. Однако это ограничено только экспортом по умолчанию:

import packageMain from 'commonjs-package' // Works
import { method } from 'commonjs-package' // Errors

К сожалению, невозможно импортировать модули ES из модулей CommonJS.

Кроме того, ESM не может импортировать файлы JSON напрямую в виде модулей, что довольно часто используется с CommonJS. Следующий оператор import завершится ошибкой:

import data from './data.json'

Это приведет к ошибке TypeError ( Неизвестное расширение файла: .json ).

Чтобы преодолеть это ограничение, мы можем снова использовать утилиту module.createRequire:

import { createRequire } from 'module'
const require = createRequire(import.meta.url)
const data = require('./data.json')
console.log(data)

В настоящее время ведется работа по встроенной поддержке модулей JSON даже в ESM, поэтому в ближайшем будущем нам может не понадобиться полагаться на createRequire() для этой функциональности.