The difference between exports and module.exports
When we write some code in a file which needs to be reused somewhere else, we’ll create a module and export the relevant piece of code.
The traditional and well-working way of using code in another files of the application is to require
the module. Before the code of the required module is executed, Node.js wraps it in a function like this:
// wrapper function
(function(exports, require, module, __filename, __dirname) {
// module code
});
As it can be seen, this wrapper function has a module
and an exports
arguments with both of which being local (i.e. specific) to the file itself.
They are not part of the global
object. Although global
has a module
property, global.module === module
will return false
if we try to log it to the console. They are not the same. The module
in question is specific to the file that is require
d.
Now that we see how both module
and exports
are generated, we can have a closer look at them.
module.exports
module
is defined as an object and has a property called exports
. The exports
property is equal to an empty object by default.
To prove this, we can create a file called main.js
and write a single line of code in it:
// main.js
console.log(module);
// returns an object with some properties, one of them being exports
Add properties to module.exports
We can add properties to the module.exports
object, just like we can do the same with any JavaScript object literal:
// bond.js
module.exports = {
myFunc: () => {
// code here
},
name: 'James Bond'
}
Here we created two properties in the module.exports
object: myFunc
, whose value is a function and name
, which is, of course, James Bond. What else could it be?
Both properties will be accessible in other files after we require
this file.
Assuming that bond.js
is in the same folder as the file where we need it (let’s call this other file main.js
for now), we can do the following at the top of main.js
:
// main.js
const myModule = require('./bond.js);
// call myFunc:
myModule.myFunc();
// log Mr Bond:
console.log(myModule.name); // James Bond
So far so good, nothing unexpected has happened.
Reassign module.exports
Because in this case exports
is a property of module
, it’s OK to make this property a function (or even a class):
// bond.js
module.exports = () => {
console.log('My name is Bond, James Bond.');
}
We reassign the default exports
property here and instead of being an object it will be a function.
In this case we need to invoke it in the file where we want to use it:
// main.js
const sayBond = require('./bond.js);
sayBond(); // My name is Bond, James Bond.
We can safely reassign module.exports
because we don’t change the behaviour of module
itself.
exports
On the other hand, we can’t do the same with exports
.
As we have seen in the module wrapper, exports
is another file specific argument just like module
. When this wrapper function is invoked upon require
-ing the file somewhere else, the value of exports
is made equal to module.exports
by default:
// something like this happens inside require()
// ...
const module = { exports: {} };
(function(module, exports) {
// code here
})(module, module.exports);
// ...
This means that initially module.exports and exports are exactly the same. exports
is just an alias or shorthand for module.exports
, hence it defaults to an empty object.
As such, we can add properties to it just like we did above with module.exports
:
exports.myFunc = () => {
// function code
}; // we can do this, myFunc is a property of the exports object
But, we cannot reassign exports
to something else, i.e. the following code does not work in the way we might expect:
exports = () => {
// function code
}; // we CANNOT do this, a new variable called exports has been created
Why? Because here we create a new variable called exports
and override the original one coming from the wrapper function.
In this case, exports
will no longer be bound to module.exports
, therefore whatever we assign exports
, it won’t be exported.
Reassigning exports
can lead to nasty and hard-to-find bugs, so this practice should be avoided.
Conclusion
module.exports
and exports
serve the same purpose as they are exactly the same. They can be used for each other without any problems with one caveat.
exports
cannot be reassigned because in that case a new variable of the same name will be created. We can, however, safely give module.exports
a new value.
Thanks for reading and I hope that the post was useful. If so, see you next time.