2

From the perspective of code maintainability, named export is better than default export because it reduces the occurrence of renaming due to references.

However, the difference between named export and default export is more than that, and there is also a big difference in logic. In order to reduce the trouble in this aspect during development, it is necessary to understand the difference in advance.

I found a good article in this area this week: export-default-thing-vs-thing-as-default , first describe the outline, and then talk about my understanding.

Overview

Generally, we believe that import imports references rather than values. That is to say, when the value of the imported object changes in the module, the value of the object imported by import should also change simultaneously.

// module.js
export let thing = 'initial';

setTimeout(() => {
  thing = 'changed';
}, 500);

In the above example, the value of the exported object is modified after 500ms.

// main.js
import { thing as importedThing } from './module.js';
const module = await import('./module.js');
let { thing } = await import('./module.js');

setTimeout(() => {
  console.log(importedThing); // "changed"
  console.log(module.thing); // "changed"
  console.log(thing); // "initial"
}, 1000);

After 1s, the output found that the first two output results have changed, and the third output has not changed. That is, for named exports, the first two are references, and the third is values.

But the default export is different:

// module.js
let thing = 'initial';

export { thing };
export default thing;

setTimeout(() => {
  thing = 'changed';
}, 500);
// main.js
import { thing, default as defaultThing } from './module.js';
import anotherDefaultThing from './module.js';

setTimeout(() => {
  console.log(thing); // "changed"
  console.log(defaultThing); // "initial"
  console.log(anotherDefaultThing); // "initial"
}, 1000);

Why is the import result of the default export a value instead of a reference?

The reason is that the default export can be regarded as a special case of "default assignment", just like export default = thing , it is essentially an assignment, so you get a value instead of a reference.

So is the same way of writing export { thing as default } Is not:

// module.js
let thing = 'initial';

export { thing, thing as default };

setTimeout(() => {
  thing = 'changed';
}, 500);
// main.js
import { thing, default as defaultThing } from './module.js';
import anotherDefaultThing from './module.js';

setTimeout(() => {
  console.log(thing); // "changed"
  console.log(defaultThing); // "changed"
  console.log(anotherDefaultThing); // "changed"
}, 1000);

It can be seen that in this default export, all exported are references. So whether an export is a reference does not depend on whether it is a named export, depends on the writing . Different writing methods have different effects, even different writing methods with the same meaning have different effects.

Is it a matter of writing? Yes, as long as export default exports are values rather than references. Unfortunately, there is a special case:

// module.js
export default function thing() {}

setTimeout(() => {
  thing = 'changed';
}, 500);
// main.js
import thing from './module.js';

setTimeout(() => {
  console.log(thing); // "changed"
}, 1000);

Why is export default function a reference? The reason is that export default function is a special case, this way of writing will lead to the export of references instead of values. If we export Function in the normal way, we still follow the previous rules:

// module.js
function thing() {}

export default thing;

setTimeout(() => {
  thing = 'changed';
}, 500);

As long as it is not written in export default function grammar, even if the exported object is a Function, the reference will not change. So what determines the effect is the wording, and has nothing to do with the type of exported object.

Regarding the issue that circular references sometimes take effect and sometimes do not take effect, it actually depends on the writing. The following circular references can work normally:

// main.js
import { foo } from './module.js';

foo();

export function hello() {
  console.log('hello');
}
// module.js
import { hello } from './main.js';

hello();

export function foo() {
  console.log('foo');
}

why? Because export function is a special case, the JS engine has made a global reference promotion to it, so both modules can be accessed separately. The following method will not work, the reason is that the global promotion will not be done:

// main.js
import { foo } from './module.js';

foo();

export const hello = () => console.log('hello');
// module.js
import { hello } from './main.js';

hello();

export const foo = () => console.log('foo');

So whether it takes effect depends on whether it is promoted, and whether it is promoted depends on how it is written. Of course, the following writing will also fail to cyclically refer to, because this writing will be parsed as an derived value:

// main.js
import foo from './module.js';

foo();

function hello() {
  console.log('hello');
}

export default hello;

The author's exploration is over here. Let's sort out our thoughts and try to understand the laws.

intensive reading

It can be understood like this:

  1. When both the export and the import are references, the final is the reference.
  2. When importing, all references {} = await import()
  3. When exporting, all references export default thing and export default 123

For import, {} = await import() equivalent to reassignment, so the reference of the specific object will be lost, that is to say, asynchronous import will be reassigned, and the reason why the reference of const module = await import() module itself is an object, and the reference of module.thing , Even if module is reassigned.

For export, the default export can be understood as the syntactic sugar export default = thing default itself is a new variable assigned, so it is reasonable that the reference of the basic type cannot be exported. Even export default '123' is legal, and export { '123' as thing } is illegal also proves this point, because the essence of named export is to assign to the default variable, you can use an existing variable to assign a value, or you can use a value directly, but named export does not have assignment, so You cannot use a literal to name the export.

There is a special case for export, export default function . We can write as little as possible. It doesn't matter if we write it, because the function keeps the reference unchanged and generally won't cause any problems.

In order to ensure that the imported is always quoted, on the one hand, try to use named import as much as possible, and on the other hand, pay attention to named export. If neither of these two points can be achieved, you can try to use Object encapsulate the variables that need to be kept referenced instead of using simple variables.

Finally, for circular dependencies, only export default function has an improved Magic, which can guarantee the normal Work of circular dependencies, but other situations are not supported. To avoid this problem, the best way is not to write circular dependencies, and use the third module as a middleman when encountering circular dependencies.

Summarize

Generally, we want to import a reference instead of an instantaneous value, but due to semantics and special syntactic sugar, not all writing effects are consistent.

I also think that there is no need to memorize these minor differences between import and export. As long as you use standardized naming import and export when writing modules, and use less default export, you can avoid these problems in terms of semantics and actual performance.

The discussion address is: Export Default/Named Export" · Issue #342 · dt-fe/weekly

If you want to participate in the discussion, please click here , a new theme every week, weekend or Monday. Front-end intensive reading-to help you filter reliable content.

Follow front-end intensive reading WeChat public

<img width=200 src="https://img.alicdn.com/tfs/TB165W0MCzqK1RjSZFLXXcn2XXa-258-258.jpg">

Copyright notice: Freely reproduced-non-commercial-non-derivative-keep the signature ( creative commons 3.0 license )

黄子毅
7k 声望9.5k 粉丝