JavaScript > ES6 and Beyond > Modules > import and export statements

ES6 Modules: Import and Export Basics

This example demonstrates the fundamental usage of import and export statements in ES6 modules. It covers both named and default exports, showcasing how to share and consume code between different JavaScript files.

Named Exports: Defining Reusable Functions

Named exports allow you to export multiple values (variables, functions, classes) from a module using their names. Each exported value must have a distinct name. This snippet defines two functions, add and subtract, and exports them using the export keyword.

// math_utils.js
export function add(x, y) {
  return x + y;
}

export function subtract(x, y) {
  return x - y;
}

Importing Named Exports: Using Functions in Another Module

To use the functions exported from math_utils.js, you use the import statement. You specify the names of the functions you want to import within curly braces {}, and the path to the module from which you are importing. Note that the path is relative to the current file.

// main.js
import { add, subtract } from './math_utils.js';

console.log(add(5, 3)); // Output: 8
console.log(subtract(10, 4)); // Output: 6

Default Export: Exporting a Primary Value

A default export is a single value that is exported from a module as its main export. A module can have only one default export. This example exports a string as the default export.

// greeting.js
const greeting = 'Hello, world!';

export default greeting;

Importing a Default Export: Using the Primary Value

When importing a default export, you can choose any name you want for the imported value. In this case, we import the default export from greeting.js and assign it to the variable message. We then print the value of message to the console.

// app.js
import message from './greeting.js';

console.log(message); // Output: Hello, world!

Mixing Named and Default Exports

A module can have both named exports and a default export. This is useful when you want to provide a primary value along with other utility functions or constants. Here, we export a multiply function as a named export and PI as the default export.

// utils.js
export function multiply(x, y) {
  return x * y;
}

const PI = 3.14159;
export default PI;

Importing Mixed Exports

To import both named and default exports from a module, you combine the syntax for importing both. The default export is specified without curly braces, and the named exports are specified within curly braces. The order doesn't matter, but it's common practice to put the default export first.

// index.js
import PI, { multiply } from './utils.js';

console.log(multiply(2, 3)); // Output: 6
console.log(PI); // Output: 3.14159

Concepts Behind the Snippet

This snippet demonstrates the core concepts of ES6 modules: named exports, default exports, and how to import them. Modules allow you to organize your code into reusable, self-contained units, which promotes code maintainability, reusability, and avoids naming conflicts.

Real-Life Use Case

Imagine building a large web application. You could have separate modules for:

  • User authentication (auth.js)
  • API requests (api.js)
  • UI components (components.js)
  • Data validation (validation.js)
Each module would export functions and constants that are used by other modules, creating a well-structured and maintainable application.

Best Practices

  • Be explicit with exports: Clearly define what you are exporting from each module.
  • Group related functionality: Organize your modules around specific features or domains.
  • Use meaningful names: Choose descriptive names for your exports and imports.
  • Avoid circular dependencies: Circular dependencies (A depends on B, and B depends on A) can lead to runtime errors.

Interview Tip

Be prepared to explain the difference between named and default exports, and when to use each type. Also, understand the benefits of using modules in general, such as code organization and reusability. You might also be asked about module bundlers like Webpack, Parcel, or Rollup and their role in packaging modules for the browser.

When to Use Them

Use ES6 modules whenever you are building a JavaScript application of any significant size. They provide a much better way to organize your code compared to older methods like using global variables or immediately invoked function expressions (IIFEs).

Memory Footprint

ES6 modules can help reduce the memory footprint of your application because module bundlers can perform tree shaking. Tree shaking is a process of removing unused code from your final bundle, which reduces the size of the bundle and improves performance. Only the exported pieces that are actually imported and used are included in the final bundle.

Alternatives

  • CommonJS: Used primarily in Node.js. Requires a different syntax (require and module.exports).
  • AMD (Asynchronous Module Definition): Used in browsers, especially with older JavaScript frameworks. Requires a more complex module definition.
  • UMD (Universal Module Definition): A pattern that attempts to be compatible with both CommonJS and AMD.
ES6 modules are now the standard and preferred approach for modern JavaScript development in both browsers and Node.js (with appropriate tooling).

Pros

  • Improved code organization: Modules promote a cleaner and more maintainable codebase.
  • Code reusability: Modules can be easily reused across different parts of your application.
  • Dependency management: Modules make it easier to manage dependencies between different parts of your code.
  • Tree shaking: Module bundlers can remove unused code, reducing the size of your application.
  • Standardized approach: ES6 modules are the standard for modern JavaScript development.

Cons

  • Requires a build process: In most browser environments, you'll need to use a module bundler (e.g., Webpack, Parcel) to package your modules into a single file (or a few files) that can be loaded by the browser. This adds complexity to your development workflow.
  • Learning curve: Understanding the nuances of import and export syntax, as well as how module bundlers work, can take some time to learn.

FAQ

  • What is the difference between named and default exports?

    Named exports allow you to export multiple values from a module using their names. Default exports allow you to export a single value as the main export of a module. You can have multiple named exports but only one default export per module.
  • Can I rename an imported value?

    Yes, you can rename an imported value using the as keyword. For example: import { add as sum } from './math_utils.js'; This would import the add function from math_utils.js and assign it to the variable sum in the current module.
  • How do I handle circular dependencies?

    Circular dependencies can be tricky. The best approach is to refactor your code to avoid them altogether. Sometimes, you can break a circular dependency by extracting the shared functionality into a separate module. If that's not possible, you might need to use dynamic imports (import()) to load modules asynchronously, which can sometimes break the cycle.