538

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.

What works

'use strict'
class SlimShady {
  constructor(options) {
    this._options = options
  }

  sayName() {
    return 'My name is Slim Shady.'
  }
}

// This works
module.exports = SlimShady

What doesn't work

'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
1
  • I still get the "SlimShady is not a constructor" error, how did you export a class as default? Commented Apr 11, 2023 at 0:29

3 Answers 3

625

The issue is with

  • how ES6 modules are emulated in CommonJS
  • how you import the module

ES6 to CommonJS

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.

Import the module

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.

Sign up to request clarification or add additional context in comments.

10 Comments

@Bergi: I didn't search tbh (shame on me :( ). There are certainly question about the same problem, but asked in a different way. Let me know if you find something that fits!
OK, it took some time to find these, but you may now use your newly acquired powers and choose one of How to correctly use ES6 “export default” with CommonJS “require”? and Can't require() default export value in Babel 6.x as a dupe target :-)
How ironic that I can do this now :D
@djKianoosh: See for yourself. After the assignment to module.exports, exports and module.exports have different values, so the assignment to exports.defaults has no effect (because module.exports is what gets exported). In other words, it's exactly the same as if you only did module.exports = { ... }.
How can we export both a default and a named value so that both these two will work on the client code side: ES6 -> import library, { a, b, c } from "library"; , commonJS -> const library = require("library"); const { a, b, c } = require("library")? Just like with React, where when using ES6 we can do import React, { useEffect, useState } from "react"; and when using commonJS we can do const React = require("react"); const { useEffect, useState } = require("react");... How can we achieve the same when authoring our own libraries? Thank you!
|
45

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()

4 Comments

So let's say initDAOneeds the object DAO. Do I have to import the current file itself? Or can I call something like this.DAO()
@Paul Since initDAO is on the DAO instance itself, you can use this to refer to the DAO object.
strange small world :)
@Mohammad indeed it is :)
1

You need to configure babel correctly in your project to use export default and export const foo

npm install --save-dev @babel/plugin-proposal-export-default-from

then add below configration in .babelrc

"plugins": [ 
       "@babel/plugin-proposal-export-default-from"
      ]

1 Comment

I believe this plugin is only for supporting the syntax export someName from './someModules.js';

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.