HomeBytesheetsRoadmapsUI TemplatesUI ToolsStack Discussion
4 Topics
Chapter•60 Minutes

Modules, Imports, Exports, and Modern JavaScript

Understand ES modules, CommonJS, module boundaries, live bindings, dynamic imports, and modern language features used in production applications.

ES Modules

ES modules provide a standardized module system using import and export. Modules have their own top-level scope, execute in strict mode, and can expose selected bindings.

Named exports and default exports have different semantics. Named exports are often preferable when a module exposes several independent concepts because import names can communicate intent.

Named and Default Exports
export function add(a, b) {
  return a + b;
}

export const version = '1.0.0';

// another file
import { add, version } from './math.js';
💡 The module exports named bindings. Another module imports those bindings explicitly.
Key Takeaways
  • ES modules create their own scope.
  • Modules execute in strict mode.
  • Named exports communicate specific module capabilities.
Knowledge Check

What keyword is used to expose a binding from an ES module?

Live Bindings

ES module imports are live bindings rather than copied snapshots. An imported binding reflects changes made to the exported binding by the exporting module. However, the importing module cannot directly reassign the imported binding.

This behavior is important for understanding shared module state and circular dependencies.

Live Export
// counter.js
export let count = 0;
export function increment() {
  count++;
}

// app.js
import { count, increment } from './counter.js';
console.log(count);
increment();
console.log(count);
💡 The imported count binding reflects the current exported binding after increment changes it.
Key Takeaways
  • ES module imports are live bindings.
  • Imported bindings cannot be reassigned by the importer.
  • Live bindings influence circular dependency behavior.
Knowledge Check

Are ES module imports simple copied values?

Dynamic import()

Dynamic import() loads a module asynchronously and returns a Promise for its module namespace object. It is useful for lazy loading, conditional functionality, and reducing initial application work.

Unlike a static import declaration, dynamic import can be placed inside expressions and functions.

Lazy Loading
async function openEditor() {
  const module = await import('./editor.js');
  module.startEditor();
}

openEditor();
💡 The editor module is requested only when openEditor runs. The Promise resolves to the module namespace.
Key Takeaways
  • dynamic import returns a Promise.
  • It enables lazy loading.
  • It can reduce initial loading work in large applications.
Knowledge Check

What does dynamic `import()` return?

CommonJS vs ES Modules

CommonJS uses require() and module.exports, while ES modules use import and export. Node.js supports both systems, but they have different loading and interoperability semantics.

ES modules are standardized by ECMAScript and support static structure that tooling can analyze. CommonJS is historically important in the Node.js ecosystem and uses a runtime-oriented loading model.

CommonJS and ESM
// CommonJS
const path = require('node:path');
module.exports = { path };

// ES Modules
import path from 'node:path';
export { path };
💡 The examples demonstrate the two module systems. Exact interoperability behavior depends on the runtime and package configuration.
Key Takeaways
  • CommonJS and ES modules are distinct module systems.
  • ES modules use import/export syntax.
  • Node.js supports both with configuration-dependent interoperability.
Knowledge Check

Which syntax belongs to CommonJS?