Demystifying NodeJs “exports” keyword

Introduction

There has been a little confusion, in my mind, about using module.exports or exports in my nodejs coding. I have seen some code on github or otherwise, many of us are using the following exports statements:

  • exports.fn = function() { … }
  • module.exports.fn = function() ( … )
  • module.exports = exports = myobject.

Then my mind wonder:

  • What is the difference between “module.exports” and “exports” ?
  • Why Nodejs has introduced module.exports as well as exports ?
  • What does it means by module.exports = exports = object ?
  • What I prefer?

In this post, I will try to answer the above four questions. First, we will give a practical sense to exports.fn, module.exports.fn & module.exports = exports statement.

So let us start our journey with codebased

Explanation

Simplified Version

The “exports” is simply a global reference to module.exports, which initially is defined as an empty object that you can add properties to. Thus, exports.fn is shorthand for module.exports.fn.

As a result, if “exports” is set to anything else, it breaks the reference between module.exports and “exports”. Because module.exports is what really gets exported, “exports” will no longer work as expected. Thus, to ensure it is referencing to the same memory location, many of us reassign the value to module.exports as well as exports at the same time.

module.exports = exports = myobject

Detailed Version

In this sesion, I will try to demonstrate the same with code.

I pre-assumed that your nodejs is available through the command prompt. If you need an installation help, please click here.

Let us create a new file in node.

-------- laptop.js -----------
 
 exports.start = function(who) {
  console.log(who + ' instance has started this laptop');
 }

// calling a method within laptop.js
module.exports.start('module.exports');

Now create a new file that will import laptop file.

-------- app.js -----------
 
 require('./laptop.js')

Call app.js from the command prompt.

c:\ node app.js

Output
-----------------------------------------------

module.exports instance has started this laptop.

As you can see that even though we have defined a function with “exports” variable, it is available through “module.exports”.

Similarly if you do it opposite that will work too.

-------- laptop.js -----------
 
 module.exports.start = function(who) {
  console.log(who + ' instance has started this laptop');
 }

exports.start('exports');

Call app.js from the command prompt.

c:\ node app.js

Output
-----------------------------------------------

exports instance has started this laptop.

Now let see what happens here:

-------- laptop.js -----------
module.exports.start = function(who) {
 console.log(who + ' instance has started this laptop');
 }
 exports.start = function(who) {
 console.log(who + ' instance has started this laptop');
 }

exports.start('exports');
module.exports.start('module.exports');

Any guess what will be outcome?

Yes, the module.exports.start has overridden by the export.start.

Call app.js from the command prompt.

c:\ node app.js
 Output
 -----------------------------------------------

 exports instance has started this laptop.
 exports instance has started this laptop.

It is clear that “exports” is an alias to “module.exports”.

We will now try to recall our  questions one by one and answer those:

1. What is the difference between exports vs module.exports ?

Node.js does not allow you to override “exports” variable with any another memory address. However you can attach N number of properties to “exports”. Thus anything that assign directly to “exports” will not be available when it is exported.

You can export anything through “module.exports” but not with”exports” keyword.

You can do this:

module.exports = function() {
 }

or

module.exports = myobject;

Basically, anything that you have exported through module.exports will be available at app.js above. However, you cannot do like this in your laptop.js and then consider it is available in app.js.

exports = function () {
 }

or

exports = myobject;

It is clear now that you can export anything (function, object, constant value) through “module.exports” but not with “exports”.

Sounds crazy? Yes it is.

2. Why they have introduced module.exports as well as exports ?

I think the main reason could be to reduce number of characters to type?

3. What does it means by module.exports = exports = object ?

Many of us set module.exports and exports at the same time, to ensure exports isn’t referencing the prior exported object. By setting both you use exports as a shorthand and avoid potential bugs later on down the road (within same file).

Here is a piece of code to make it:

------ laptop.js ------

exports = "exports";
module.exports = "module.exports";

console.log(exports);
console.log(module.exports);

Call app.js from the command prompt.

c:\ node app.js
 Output
 -----------------------------------------------
exports
module.exports

Because they are pointing to a different location and by any chance the module has decided to use “exports” (not “module.exports” variable) value after it is set it will not be in sync.

Thus, to make it in sync. it is advisable that the “exports” variable has been set by anything you would want to define a rule that whenever we set module.exports with any value set the same value to exports in the same line.

Here is an example:

------ laptop.js ------
 
exports = 'i am value ';
module.exports = exports = function() {
   console.log('function is called.');
 }
console.log(typeof exports)
-------- app.js --------

var laptop = require('./lib/laptop.js');
laptop();

 

Call app.js from the command prompt.

c:\ node app.js
 Output
 -----------------------------------------------
function
function is called.

You can see in the output that because “exports” as well as “module.exports” are set to a function the output of “typeof” statement is “function”. If you don’t assign a function to exports then the typeof statement will produce “string”.

Thus, to remove any potential bugs we decide to set exports as well as “module.exports” at the same time.

Now this discussion is coming to an end with the last question i.e.

What I prefer?

Personally, I prefer to export a constructor function that can be used create an instance of a module.

example:

------ laptop.js ------

var laptop = function() {}
laptop.prototype.start = function() {};
laptop.prototype.stop = function() {};
laptop.prototype.suspend = function() {};
module.exports = exports = laptop;
 ------ app.js ------
var laptop = require('./laptop')
var mac = new laptop();
var win = new laptop();

However, if I want to give a singleton object then I replace the following line in laptop.js

------ laptop.js ------

module.exports = exports = new laptop();

and in app.js

 ------ app.js ------

var mac = require('./laptop');
mac.start();

Conclusion

  • We understand now that the exports is an alias to module.exports that can shorthand your writing in module development.
  • It is recommended that we point exports alias to module.exports value. Thus you should set exports whenever you are setting module.exports.
  • Since my background is .net, I recommend to export a class (constructor function) or an object.

– Happy coding!