332

On this page (http://docs.nodejitsu.com/articles/getting-started/what-is-require), it states that "If you want to set the exports object to a function or a new object, you have to use the module.exports object."

My question is why.

// right
module.exports = function () {
  console.log("hello world")
}
// wrong
exports = function () {
  console.log("hello world")
}

I console.logged the result (result=require(example.js)) and the first one is [Function] the second one is {}.

Could you please explain the reason behind it? I read the post here: module.exports vs exports in Node.js . It is helpful, but does not explain the reason why it is designed in that way. Will there be a problem if the reference of exports be returned directly?

Samuel Liew
  • 76,741
  • 107
  • 159
  • 260
Devs love ZenUML
  • 11,344
  • 8
  • 53
  • 67
  • 17
    Always use `module.exports`. – Gabriel Llamas May 05 '13 at 11:04
  • 1
    I think following above mentioned advice allows to avoid this problem. – Vitalii Korsakov Sep 26 '13 at 10:30
  • 1
    @GabrielLlamas so why do many packages use just `exports`, for example https://github.com/tj/consolidate.js/blob/master/lib/consolidate.js? – CodyBugstein Feb 09 '15 at 07:48
  • 4
    @Imray If you always use `module.exports`, you'll never be wrong, but you can use `exports` if you're not replacing the default exported object, that is, if you simply attach properties like this: `var foo = require('foo').foo`. This `foo` property can be exported like this: `exports.foo = ...` and of course also with `module.exports`. It's a personal choice but I'm currently using `module.exports` and `exports` appropriately. – Gabriel Llamas Feb 09 '15 at 09:13
  • I prefer exports.myFunc = function() {} so I don't have to maintain a list of exports at the bottom of the file. It feels closer to the common practice of exporting when you declare in ES6. – SacWebDeveloper Oct 07 '19 at 07:20

9 Answers9

732

module is a plain JavaScript object with an exports property. exports is a plain JavaScript variable that happens to be set to module.exports. At the end of your file, node.js will basically 'return' module.exports to the require function. A simplified way to view a JS file in Node could be this:

var module = { exports: {} };
var exports = module.exports;

// your code

return module.exports;

If you set a property on exports, like exports.a = 9;, that will set module.exports.a as well because objects are passed around as references in JavaScript, which means that if you set multiple variables to the same object, they are all the same object; so then exports and module.exports are the same object.
But if you set exports to something new, it will no longer be set to module.exports, so exports and module.exports are no longer the same object.

goto-bus-stop
  • 11,655
  • 2
  • 24
  • 31
  • 21
    Right, it's just basics of reference types. – Vitalii Korsakov Sep 26 '13 at 10:32
  • 7
    Great explanation. The documentation for `module.exports` describes it too: https://nodejs.org/api/modules.html#modules_module_exports – Brian Morearty May 01 '19 at 04:38
  • 2
    here is documentation for exports: https://nodejs.org/api/modules.html#modules_exports_shortcut, which gave me another view of understanding – apollo May 23 '19 at 03:37
  • Thanks, but why we do need both `exports` and `module.exports`? Can't we just live with (i.e.) the latter, for sake of simplicity (and KISS principle)? – zambotn May 20 '20 at 14:52
  • 1
    There is strictly not a need for `exports`, it's just a handy shorthand. But now it can never be removed because it would break all existing code. – goto-bus-stop May 26 '20 at 18:32
78

Renee's answer is well explained. Addition to the answer with an example:

Node does a lot of things to your file and one of the important is WRAPPING your file. Inside nodejs source code "module.exports" is returned. Lets take a step back and understand the wrapper. Suppose you have

greet.js

var greet = function () {
   console.log('Hello World');
};

module.exports = greet;

the above code is wrapped as IIFE(Immediately Invoked Function Expression) inside nodejs source code as follows:

(function (exports, require, module, __filename, __dirname) { //add by node

      var greet = function () {
         console.log('Hello World');
      };

      module.exports = greet;

}).apply();                                                  //add by node

return module.exports;                                      //add by node

and the above function is invoked (.apply()) and returned module.exports. At this time module.exports and exports pointing to the same reference.

Now, imagine you re-write greet.js as

exports = function () {
   console.log('Hello World');
};
console.log(exports);
console.log(module.exports);

the output will be

[Function]
{}

the reason is : module.exports is an empty object. We did not set anything to module.exports rather we set exports = function()..... in new greet.js. So, module.exports is empty.

Technically exports and module.exports should point to same reference(thats correct!!). But we use "=" when assigning function().... to exports, which creates another object in the memory. So, module.exports and exports produce different results. When it comes to exports we can't override it.

Now, imagine you re-write (this is called Mutation) greet.js (referring to Renee answer) as

exports.a = function() {
    console.log("Hello");
}

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

the output will be

{ a: [Function] }
{ a: [Function] }

As you can see module.exports and exports are pointing to same reference which is a function. If you set a property on exports then it will be set on module.exports because in JS, objects are pass by reference.

Conclusion is always use module.exports to avoid confusion. Hope this helps. Happy coding :)

Sdembla
  • 1,629
  • 13
  • 13
  • This too, is a beautiful insightful answer and complements @goto-bus-stop 's answer. :) – varun Aug 31 '19 at 14:32
30

Also, one things that may help to understand:

math.js

this.add = function (a, b) {
    return a + b;
};

client.js

var math = require('./math');
console.log(math.add(2,2); // 4;

Great, in this case:

console.log(this === module.exports); // true
console.log(this === exports); // true
console.log(module.exports === exports); // true

Thus, by default, "this" is actually equals to module.exports.

However, if you change your implementation to:

math.js

var add = function (a, b) {
    return a + b;
};

module.exports = {
    add: add
};

In this case, it will work fine, however, "this" is not equal to module.exports anymore, because a new object was created.

console.log(this === module.exports); // false
console.log(this === exports); // true
console.log(module.exports === exports); // false

And now, what will be returned by the require is what was defined inside the module.exports, not this or exports, anymore.

Another way to do it would be:

math.js

module.exports.add = function (a, b) {
    return a + b;
};

Or:

math.js

exports.add = function (a, b) {
    return a + b;
};
Rodrigo Branas
  • 538
  • 5
  • 10
17

Rene's answer about the relationship between exports and module.exports is quite clear, it's all about javascript references. Just want to add that:

We see this in many node modules:

var app = exports = module.exports = {};

This will make sure that even if we changed module.exports, we can still use exports by making those two variables point to the same object.

Siderite Zackwehdex
  • 6,293
  • 3
  • 30
  • 46
DevGrowth.Tech
  • 1,523
  • 17
  • 23
  • I became confused with this explanation, kind to elaborate? – GuyFreakz Feb 27 '17 at 13:58
  • 6
    @GuyFreakz I'm not sure if this speaks to your confusion, but `module.exports` and `exports` are just separate variables, initialized to reference the same object. If you change what one variable references, the two variables no longer reference the same thing. The line of code above ensures both variables are initialized to the same new object. – Andrew Palmer Mar 06 '17 at 13:53
  • An actual use case which everyone else missed out on @fengshuo. Thanks! – Aakash Verma Jan 20 '18 at 14:08
4

node does something like this:

module.exports = exports = {}

module.exports and exports refer to same object.

This is done just for convenience. so instead of writing something like this

module.exports.PI = 3.14

we can write

exports.PI = 3.14

so it is ok to add a property to exports but assigning it to a different object is not ok

exports.add = function(){
.
.
} 

↑ this is OK and same as module.exports.add = function(){...}

exports = function(){
.
.
} 

↑ this is not ok and and empty object will be returned as module.exports still refers to {} and exports refer to different object.

Ankit Aabad
  • 115
  • 1
  • 4
2

There are two difference between module.exports and exports

  1. When export a single class, variable or function from one module to another module, we use the module.exports. But export to multiple variables or functions from one module to another, we use exports.

  2. module.exports is the object reference that gets returned from the require() calls. But exports is not returned by require().

see more details with examples >> link

Janitha Rasanga
  • 1,010
  • 1
  • 11
  • 20
1

As all answers posted above are well explained, I want to add something which I faced today.

When you export something using exports then you have to use it with variable. Like,

File1.js

exports.a = 5;

In another file

File2.js

const A = require("./File1.js");
console.log(A.a);

and using module.exports

File1.js

module.exports.a = 5;

In File2.js

const A = require("./File1.js");
console.log(A.a);

and default module.exports

File1.js

module.exports = 5;

in File2.js

const A = require("./File2.js");
console.log(A);
Dharman
  • 30,962
  • 25
  • 85
  • 135
bhargav3vedi
  • 521
  • 1
  • 6
  • 11
0

myTest.js

module.exports.get = function () {};

exports.put = function () {};

console.log(module.exports)
// output: { get: [Function], put: [Function] }

exports and module.exports are the same and a reference to the same object. You can add properties by both ways as per your convenience.

simonwo
  • 3,171
  • 2
  • 19
  • 28
Shashwat Gupta
  • 5,071
  • 41
  • 33
0

You can think of exports as a shortcut to module.exports within a given module. In fact, exports is just a variable that gets initialized to the value of module.exports before the module is evaluated. That value is a reference to an object (empty object in this case). This means that exports holds a reference to the same object referenced by module.exports. It also means that by assigning another value to exports it's no longer bound to module.exports.

This explanation from MDN is the most clear to me.

Basically, there is one object in memory which is referenced by 2 variables - exports and module.exports.

exports.a = 23

equals

module.exports = {a:23}

But,

exports = {a:23}

does not equal

module.exports = {a:23}

When you assign a new object to exports variable directly, then that variable does not refer to module.exports anymore.

ktul
  • 1