Understanding CommonJS (CJS) Module System: module.exports vs exports
In the world of Node.js, the CommonJS (CJS) module system is the default method for organizing and exporting functionality. Two key elements that often create confusion for developers — especially beginners — are module.exports and exports. Both are used to export functionality from a module, but understanding their differences is crucial for working effectively in Node.js.
In this post, we’ll break down the differences between module.exports and exports, explore when and how to use them, and highlight some best practices to avoid common pitfalls.
What Are module.exports and exports?
Before diving into the differences, it’s important to understand that both module.exports and exports are used to export values from a Node.js module.
module.exportsis the official object that gets returned when a module isrequire()d in another file.exportsis a shorthand reference tomodule.exports, making it easier to add multiple properties to the exported object.
Let’s break this down further with examples to clarify how they work.
1. module.exports - The Official Export Object
The module.exports is the actual object that gets returned when a module is imported using require(). This is the official export object, and assigning any value directly to it will replace whatever was previously assigned. Whether you're exporting a single function, an object, or an array, you assign it directly to module.exports.
Example: Exporting a Single Function
// math.js
module.exports = function add(a, b) {
return a + b;
};
// main.js
const add = require('./math');
console.log(add(2, 3)); // Output: 5Here, we’re directly assigning the add function to module.exports. When require('./math') is called in main.js, the add function is returned.
Key Takeaway:
If you want to export a single entity — like a function, class, or object — you should assign it directly to module.exports. This way, the entire exported module is replaced by that entity.
2. exports - The Shortcut to module.exports
In CommonJS, exports is a reference to module.exports, making it easier to add multiple properties to the module without directly manipulating the module.exports object.
You can add properties to exports, which will automatically be added to module.exports since they both refer to the same object.
Example: Exporting Multiple Properties
// math.js
exports.add = function(a, b) {
return a + b;
};
exports.subtract = function(a, b) {
return a - b;
};
// main.js
const math = require('./math');
console.log(math.add(2, 3)); // Output: 5
console.log(math.subtract(5, 3)); // Output: 2In this example, exports.add and exports.subtract are added to the module. When require('./math') is called in main.js, both functions are accessible.
Key Takeaway:
Use exports when you want to add multiple properties or functions to your module, rather than replacing the entire module export.
Key Differences Between module.exports and exports
While both module.exports and exports are used to export values, there are critical differences in how they work:
- Assignment to
exports:
- If you use
exportsto add properties to the module, it works fine becauseexportsis just a reference tomodule.exports. - However, if you assign a new value to
exports(e.g.,exports = {}), it breaks the connection tomodule.exports. This can lead to unexpected behavior and errors.
// math.js
exports = { add: function(a, b) { return a + b; } };
// main.js will not work correctly because exports is now
// detached from module.exports
const math = require('./math');
console.log(math.add(2, 3)); // Undefined or error2. Overriding module.exports:
- When you directly assign to
module.exports, you completely replace the export object, which is useful when you want to export a single entity, such as a function or class.
// math.js
module.exports = function add(a, b) { return a + b; };This approach completely replaces the previous content of module.exports, which can be helpful in cases where the module represents a single function or object.
3. Combining module.exports and exports:
- While you can use both
module.exportsandexportstogether, doing so can create confusion. If you assign a value directly tomodule.exports, it will overrideexports, making it a detached reference.
// math.js
module.exports = function(a, b) { return a + b; };
exports.add = function(a, b) { return a - b; }; // This doesn't workIn this case, the assignment to module.exports overrides the exports reference, and the add function will not be exported.
Best Practices for Using module.exports and exports
To avoid common mistakes, here are some best practices for using module.exports and exports:
- Use
module.exportswhen you want to export a single entity like a function, object, or class. This ensures clarity and avoids overwriting issues.
// math.js
module.exports = function add(a, b) { return a + b; };- Use
exportswhen you want to export multiple functions or properties. This allows you to easily add various exports without replacing the entire export object.
// math.js
exports.add = function(a, b) { return a + b; };
exports.subtract = function(a, b) { return a - b; };- Avoid combining
module.exportsandexportsin the same module unless you have a clear understanding of how both work. If you assign a new value toexports, it will break the reference tomodule.exports. - Be cautious with reassigning
exports. If you assign a new object toexports, it detaches it frommodule.exportsand can result in unexpected behavior.
Conclusion
Understanding the differences between module.exports and exports is crucial for building maintainable and error-free modules in Node.js. By adhering to best practices and understanding when to use each method, you can avoid common pitfalls and improve the readability and structure of your code. Whether you're working with simple exports or complex module systems, mastering these tools will help you take full advantage of the CommonJS module system.
Remember: module.exports is for single exports, and exports is for adding multiple properties—just make sure not to overwrite exports if you want to maintain proper module exports!
