Node.js - use of module.exports as a constructor
CommonJS modules allow two ways to define exported properties. In either case you are returning an Object/Function. Because functions are first class citizens in JavaScript they to can act just like Objects (technically they are Objects). That said your question about using the new
keywords has a simple answer: Yes. I'll illustrate...
Module exports
You can either use the exports
variable provided to attach properties to it. Once required in another module those assign properties become available. Or you can assign an object to the module.exports property. In either case what is returned by require()
is a reference to the value of module.exports
.
A pseudo-code example of how a module is defined:
var theModule = {
exports: {}
};
(function(module, exports, require) {
// Your module code goes here
})(theModule, theModule.exports, theRequireFunction);
In the example above module.exports
and exports
are the same object. The cool part is that you don't see any of that in your CommonJS modules as the whole system takes care of that for you all you need to know is there is a module object with an exports property and an exports variable that points to the same thing the module.exports does.
Require with constructors
Since you can attach a function directly to module.exports
you can essentially return a function and like any function it could be managed as a constructor (That's in italics since the only difference between a function and a constructor in JavaScript is how you intend to use it. Technically there is no difference.)
So the following is perfectly good code and I personally encourage it:
// My module
function MyObject(bar) {
this.bar = bar;
}
MyObject.prototype.foo = function foo() {
console.log(this.bar);
};
module.exports = MyObject;
// In another module:
var MyObjectOrSomeCleverName = require("./my_object.js");
var my_obj_instance = new MyObjectOrSomeCleverName("foobar");
my_obj_instance.foo(); // => "foobar"
Require for non-constructors
Same thing goes for non-constructor like functions:
// My Module
exports.someFunction = function someFunction(msg) {
console.log(msg);
}
// In another module
var MyModule = require("./my_module.js");
MyModule.someFunction("foobar"); // => "foobar"
In my opinion, some of the node.js examples are quite contrived.
You might expect to see something more like this in the real world
// square.js
function Square(width) {
if (!(this instanceof Square)) {
return new Square(width);
}
this.width = width;
};
Square.prototype.area = function area() {
return Math.pow(this.width, 2);
};
module.exports = Square;
Usage
var Square = require("./square");
// you can use `new` keyword
var s = new Square(5);
s.area(); // 25
// or you can skip it!
var s2 = Square(10);
s2.area(); // 100
For the ES6 people
class Square {
constructor(width) {
this.width = width;
}
area() {
return Math.pow(this.width, 2);
}
}
export default Square;
Using it in ES6
import Square from "./square";
// ...
When using a class, you must use the new
keyword to instatiate it. Everything else stays the same.
This question doesn't really have anything to do with how require()
works. Basically, whatever you set module.exports
to in your module will be returned from the require()
call for it.
This would be equivalent to:
var square = function(width) {
return {
area: function() {
return width * width;
}
};
}
There is no need for the new
keyword when calling square
. You aren't returning the function instance itself from square
, you are returning a new object at the end. Therefore, you can simply call this function directly.
For more intricate arguments around new
, check this out: Is JavaScript's "new" keyword considered harmful?