export default vs module.exports differences

curly bracket imports {} are only for named exports. for default export you are not expected to use them during import. if you want still use {} for your 3rd case this should work.

import {default as bar } from './foo';
bar();

// foo.js
export default {
  bar() {}
}

When you have

export default {
  bar() {}
}

The actual object exported is of the following form:

exports: {
  default: {
    bar() {}
  }
}

When you do a simple import (e.g., import foo from './foo';) you are actually getting the default object inside the import (i.e., exports.default). This will become apparent when you run babel to compile to ES5.

When you try to import a specific function (e.g., import { bar } from './foo';), as per your case, you are actually trying to get exports.bar instead of exports.default.bar. Hence why the bar function is undefined.

When you have just multiple exports:

export function foo() {};
export function bar() {};

You will end up having this object:

exports: {
  foo() {},
  bar() {}
}

And thus import { bar } from './foo'; will work. This is the similar case with module.exports you are essentially storing an exports object as above. Hence you can import the bar function.

I hope this is clear enough.