What is the difference between Node's module.exports
and ES6's export default
? I'm trying to figure out why I get the "__ is not a constructor" error when I try to export default
in Node.js 6.2.2.
'use strict' class SlimShady { constructor(options) { this._options = options } sayName() { return 'My name is Slim Shady.' } } // This works module.exports = SlimShady
'use strict' class SlimShady { constructor(options) { this._options = options } sayName() { return 'My name is Slim Shady.' } } // This will cause the "SlimShady is not a constructor" error // if in another file I try `let marshall = new SlimShady()` export default SlimShady
With the help of ES6, we can create modules in JavaScript. In a module, there can be classes, functions, variables, and objects as well. To make all these available in another file, we can use export and import. The export and import are the keywords used for exporting and importing one or more members in a module.
ES6 provides two ways to export a module from a file: named export and default export. With named exports, one can have multiple named exports per file. Then import the specific exports they want surrounded in braces. The name of imported module has to be the same as the name of the exported module.
In summary, named exports are used to export multiple values. During the import, it will be possible to use the same name to refer to the exported value. Default exports are used to export a single value from the file. During the import, the name of the value can be different from the exported one.
The issue is with
At the time of writing this, no environment supports ES6 modules natively. When using them in Node.js you need to use something like Babel to convert the modules to CommonJS. But how exactly does that happen?
Many people consider module.exports = ...
to be equivalent to export default ...
and exports.foo ...
to be equivalent to export const foo = ...
. That's not quite true though, or at least not how Babel does it.
ES6 default
exports are actually also named exports, except that default
is a "reserved" name and there is special syntax support for it. Lets have a look how Babel compiles named and default exports:
// input export const foo = 42; export default 21; // output "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var foo = exports.foo = 42; exports.default = 21;
Here we can see that the default export becomes a property on the exports
object, just like foo
.
We can import the module in two ways: Either using CommonJS or using ES6 import
syntax.
Your issue: I believe you are doing something like:
var bar = require('./input'); new bar();
expecting that bar
is assigned the value of the default export. But as we can see in the example above, the default export is assigned to the default
property!
So in order to access the default export we actually have to do
var bar = require('./input').default;
If we use ES6 module syntax, namely
import bar from './input'; console.log(bar);
Babel will transform it to
'use strict'; var _input = require('./input'); var _input2 = _interopRequireDefault(_input); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } console.log(_input2.default);
You can see that every access to bar
is converted to access .default
.
Felix Kling did a great comparison on those two, for anyone wondering how to do an export default alongside named exports with module.exports in nodejs
module.exports = new DAO() module.exports.initDAO = initDAO // append other functions as named export // now you have let DAO = require('_/helpers/DAO'); // DAO by default is exported class or function DAO.initDAO()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With