exports vs module.exports in node.js

I was confused about how require function works in node.js for a long time. I found when I require a module, sometimes I can get the object I want, but sometimes, I don’t I just got an empty object, which give an imagination that we cannot export the object by assigning it to exports, but it seems somehow we can export a function by assignment.

Today, I re-read the document again, and I finally make clear that I misunderstood the “require” mechanism and how I did that.

I clearly remember this sentence in the doc

In particular module.exports is the same as the exports object.

So I believed that the exports is just a shortcut alias to module.exports, we can use one instead of another without worrying about the differences between them two.
But this understanding is proved to be wrong. exports and module.exports are different.

Today I found this in the doc:

The exports object is created by the Module system. Sometimes this is not acceptable, many want their module to be an instance of some class. To do this assign the desired export object to module.exports.

So it says that module.exports is different from exports. And it you exports something by assignment, you need to assign it to module.exports.

Let’s try to understand these sentences deeper by code examples.

In the saying

The exports object is created by the Module system.

The word “created by” actually means when node.js try to load a javascript file, before executing any line of code in your file, the module system executes the following code first for you:

1
var exports = module.exports

So the actual interface in node.js’s module system is module object. the actual exported object is module.exports not exports.
And the exports is just a normal variable, and there is not “magic” in it. So if you assign something to it, it is replaced absolutely.

That’s why I failed to get the exported object I want when I assign the it to exports variable.

So to export some variable as a whole, we should always assign it to module.exports.
And at same time, if there is no good excuse, we’d better to keep the convention that exports is the shortcut alias to module.exports. So we should also assign the module.exports to exports.

As a conclusion, to export something in node.js by assignment, we should always follow the following pattern:

1
2
3
exports = module.exports = {
...
}

A way to expose singleton object and its constructor in node.js

In Node.js world, we usually encapsulate a service into a module, which means the module need to export the façade of the service. In most case the service could be a singleton, all apps use the same service.

But in some rare cases, people might would like to create several instances of the service ,which means the module also need to also export the service constructor.

A very natural idea is to export the default service, and expose the constructor as a method of the default instance. So we could consume the service in this way:

Ideal Usage
1
2
var defaultService = require('service');
var anotherService = service.newService();

So we need to write the module in this way:

Ideal Export
1
2
3
4
5
function Service() { }
module.exports = new Service();
moudle.exports.newService = Service;

But for some reason, node.js doesn’t allow module to expose object by assigning the a object to module.exports.
To export a whole object, it is required to copy all the members of the object to moudle.exports, which drives out all kinds of tricky code.

I misunderstood how node.js require works, and HERE is the right understanding. Even I misunderstood the mechanism, but the conclusion of this post is still correct. To export function is still a more convenient way to export both default instance and the constructor.

And things can become much worse when there are backward reference from the object property to itself.
So to solve this problem gracefully, we need to change our mind.
Since it is proved that it is tricky to export a object, can we try to expose the constructor instead?

Then answer is yes. And Node.js does allow we to assign a function to the module.exports to exports the function.
So we got this code.

Export Constructor
1
2
function Service() { }
module.exports = Service;

So we can use create service instance in this way:

Create Service
1
2
var Service = require('service');
var aService = new Service();

As you see, since the one we exported is constructor so we need to create a instance manually before we can use it. Another problem is that we lost the shared instance between module users, and it is a common requirement to share the same service instance between users.

How to solve this problem? Since as we know, function is also kind of object in javascript, so we can kind of add a member to the constructor called default, which holds the shared instance of the service.

This solution works but not in a graceful way! A crazy but fancy idea is that can we transform the constructor itself into kind of singleton instance??!! Which means you can do this:

Export Singleton
1
2
3
4
var defaultService = require('service');
defaultService.foo();
var anotherService = service();
anotherService.foo();

The code style looks familiar? Yes, jQuery, and many other well-designed js libraries are designed to work in this way.
So our idea is kind of feasible but how?

Great thank to Javascript’s prototype system (or maybe SELF’s prototype system is more accurate.), we can simply make a service instance to be the constructor’s prototype.

Actual Export
1
2
3
function Service() { }
module.exports = Service;
Service.__proto__ = new Serivce;

Sounds crazy, but works, and gracefully! That’s the beauty of Javascript.