ChatGPT解决这个技术问题 Extra ChatGPT

module.exports vs. export default in Node.js and ES6

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

F
Felix Kling

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.


@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!
m
moein rahimi

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

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.
H
Hassan Azhar Khan

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"
      ]

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