Why does JavaScript have default exports? - javascript

Context
JavaScript has two types of exports: normal[1] and default.
EDIT: JavaScript has two types of export syntaxes
Normal exports
class Foo {...}
class Bar {...}
export {
Foo,
Bar,
};
or
export class Foo {...}
export class Bar {...}
Normal exports can be imported in two ways: namespace[1] imports and named imports (looks similar to destructuring).
Namespace imports
import * as Baz from './baz';
Named imports
import {Foo, Bar} from './baz';
Default exports
class Foo {...}
class Bar {...}
export default {
Foo,
Bar,
};
Default exports can also be imported in two ways: namespace imports and named imports (in a hackish way using destructuring in a separate statement)
Namespace imports
import Baz from './baz';
Named imports
import temp_Baz from './baz';
const {Foo, Bar} = temp_Baz;
Question
Both normal exports and default exports have the same functionality - they can be imported into a namespace, and they can be destructured into smaller pieces.
1) What are the reasons JavaScript has default exports, instead of sticking with just normal exports and having the import syntax be the following?
import <varName> from <location>;
<varName> would allow destructuring like any variable assignment, without a special "named" import.
Node.js managed without default exports, and the only additional change would be to allow exporting of a single value:
module.exports = class Foo {...}
This could be done with export = or export only or something similar:
export only class Foo {...}
So now you might ask, what is the difference between export only and export default?
export only exports could be imported with the same syntax, whereas export default requires a different importing syntax. To allow both syntaxes to work for the module user, the module must both export and export default the same items (which is something I do in all my modules).
export default is renamed to export only which is a better term in my opinion (less confusing; clearer)
2) All the differences listed above are pros. Are there any other differences that are cons? Or can any of the above differences also be cons?
Edit
It seems I misunderstood the intended use for export default.
It's for adding a default export on top of the regular exports.
So I'd now like to know when are default exports used? Although I probably shouldn't add more to this question.
If the only use for default exports is if your module only has a single item to export, then this question still applies.
Edit 2
Now it seems that the intended use is if a module only exports a single item. So my question still applies. Why not replace default exports with export only, removing the need for an additional default import syntax?
Notes
[1] I am not sure if this is the correct term to use

The existence of "default exports" as well as "named exports" is due to design by committee. This was convincingly argued by Andreas Rossberg during the original design discussions:
Some want modules in the conventional sense, as encapsulated namespaces with named exports, which you can import and access qualified or unqualified, and where imports are checked.
Some want modules in the specific pre-ES6 JavaScript style, where they can be any arbitrary JS value, and in the good old JavaScript tradition that checking doesn't matter.
The current design (including the newest suggestions for imports) seems to please neither side, because it tries to be the former semantically, but wants to optimise for the latter syntactically. The main outcome is confusion.
That is, the motivation for "default exports" was not technical, but political, designed to appease those in the CommonJS and AMD worlds, where a module exports a single value. This neatly explains why we have two module systems in one; something we don't see in other programming languages.
(Thanks a lot to Bergi, who found and cited this thread in his answer. However, I disagree with his conclusion that default exports are motivated by a need for a shorthand. I don't see much discussion of this in the original threads.)

JavaScript has two types of exports: normal and default.
No, not really. JavaScript has only one kind of export structure. It has different syntaxes though.
export default function x() {} is just a shorthand notation for function x(){}; export { x as default }.
import x from '…' is just a shorthand notation for import { default as x } from '…'.
The difference between your the two module exports you discussed is much bigger than the single default keyword. They only look so similar because you used shorthand notations. Fully spelled out, it's
export {
Foo as Foo,
Bar as Bar,
} // a declaration
vs
export default ({
Foo: Foo,
Bar: Bar,
}); // an object literal
Both normal exports and default exports have the same functionality - they can be imported into a namespace, and they can be destructured into smaller pieces.
No, they don't. Imports provide aliases for the exported variable declarations, there's no destructuring - and you cannot do that to properties of a default-exported object.
What are the reasons JavaScript has default exports, instead of sticking with just normal exports and having the import syntax be the following?
See https://esdiscuss.org/topic/moduleimport. The default-export syntax provides a shorthand syntax for exporting under the special name default (which is not a valid variable name otherwise), as it is a quite common use case to export only a single thing from a module. It doesn't require explicitly naming a value when there is only one in this module anyway. In this regard, it has very much the purpose you envisioned for your export only suggestion, but it is only one export not providing a full namespace of multiple aliasable exports.

export default {
Foo,
Bar,
}
I don't understand why you've demonstrated this structure. This isn't how it's supposed to be used.
The default export really be though of as just another export. If you import * as Package from "package" you'll realise... Package.default is the default export
The only change is the syntactic shortcut:
import alias from "package" = import {default as alias} from "package"
It makes consumption of many packages easier / simpler.
export = ... is not the same as export default ...
The prior returns a module structure of ... where as latter returns a module structure of {default: ...}

Related

What's difference between name and {name} in react? [duplicate]

I have referred all the questions in stackoverflow.
But none of the suggested why and when to use default export.
I just saw that default can be metioned "When there is only one export in a file"
Any other reason for using default export in es6 modules?
Some differences that might make you choose one over the other:
Named Exports
Can export multiple values
MUST use the exported name when importing
Default Exports
Export a single value
Can use any name when importing
This article does a nice job of explaining when it would be a good idea to use one over the other.
It's somewhat a matter of opinion, but there are some objective aspects to it:
You can have only one default export in a module, whereas you can have as many named exports as you like.
If you provide a default export, the programmer using it has to come up with a name for it. This can lead to inconsistency in a codebase, where Mary does
import example from "./example";
...but Joe does
import ex from "./example";
In contrast, with a named export, the programmer doesn't have to think about what to call it unless there's a conflict with another identifier in their module.¹ It's just
import { example } from "./example";
With a named export, the person importing it has to specify the name of what they're importing. They get a nice early error if they try to import something that doesn't exist.
If you consistently only use named exports, programmers importing from modules in the project don't have to think about whether what they want is the default or a named export.
¹ If there is a conflict (for instance, you want example from two different modules), you can use as to rename:
import { example as widgetExample } from "./widget/example";
import { example as gadgetExample } from "./gadget/example";
You should almost always favour named exports, default exports have many downsides
Problems with default exports:
Difficult to refactor or ensure consistency since they can be named anything in the codebase other than what its actually called
Difficult to analyze by automated tools or provide code intellisense and autocompletion
They break tree shaking as instead of importing the single function you want to use you're forcing webpack to import the entire file with whatever other dead code it has leading to bigger bundle sizes
You can't export more than a single export per file
You lose faster/direct access to imports
checkout these articles for a more detailed explanation:
https://blog.neufund.org/why-we-have-banned-default-exports-and-you-should-do-the-same-d51fdc2cf2ad
https://humanwhocodes.com/blog/2019/01/stop-using-default-exports-javascript-module/
https://rajeshnaroth.medium.com/avoid-es6-default-exports-a24142978a7a
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.
// imports
// ex. importing a single named export
import { MyComponent } from "./MyComponent";
// ex. importing multiple named exports
import { MyComponent, MyComponent2 } from "./MyComponent";
// ex. giving a named import a different name by using "as":
import { MyComponent2 as MyNewComponent } from "./MyComponent";
// exports from ./MyComponent.js file
export const MyComponent = () => {}
export const MyComponent2 = () => {}
You can also alias named imports, assign a new name to a named export as you import it, allowing you to resolve naming collisions, or give the export a more informative name.
import MyComponent as MainComponent from "./MyComponent";
You can also Import all the named exports onto an object:
import * as MainComponents from "./MyComponent";
// use MainComponents.MyComponent and MainComponents.MyComponent2 here
One can have only one default export per file. When we import we have to specify a name and import like:
// import
import MyDefaultComponent from "./MyDefaultExport";
// export
const MyComponent = () => {}
export default MyComponent;
The naming of import is completely independent in default export and we can use any name we like.
From MDN:
Named exports are useful to export several values. During the import, one will be able to use the same name to refer to the corresponding value.
Concerning the default export, there is only a single default export per module. A default export can be a function, a class, an object or anything else. This value is to be considered as the “main” exported value since it will be the simplest to import.
There aren't any definitive rules, but there are some conventions that people use to make it easier to structure or share code.
When there is only one export in the entire file, there is no reason to make it named.
Also, when your module has one main purpose, it could make sense to make that your default export. In those cases you can extra named exports
In react for example, React is the default export, since that is often the only part that you need. You don't always Component, so that's a named export that you can import when needed.
import React, {Component} from 'react';
In the other cases where one module has multiple equal (or mostly equal) exports, it's better to use named exports
import { blue, red, green } from 'colors';
1st Method:-
export foo; //so that this can be used in other file
import {foo} from 'abc'; //importing data/fun from module
2nd Method:-
export default foo; //used in one file
import foo from 'blah'; //importing data/fun from module
3rd Method:-
export = foo;
import * as foo from 'blah';
The above methods roughly compile to the following syntax below:-
//all export methods
exports.foo = foo; //1st method
exports['default'] = foo; //2nd method
module.exports = foo; //3rd method
//all import methods
var foo = require('abc').foo; //1st method
var foo = require('abc')['default']; //2nd method
var foo = require('abc'); //3rd method
For more information, visit to Default keyword explaination
Note:- There can be only one export default in one file.
So whenever we are exporting only 1 function, then it's better to use default keyword while exporting
EASIEST DEFINITION TO CLEAR CONFUSIONS
Let us understand the export methods, first, so that we can analyze ourselves when to use what, or why do we do what we do.
Named exports: One or more exports per module. When there are more than one exports in a module, each named export must be restructured while importing. Since there could be either export in the same module and the compiler will not know which one is required unless we mention it.
//Named export , exporting:
export const xyz = () =>{
}
// while importing this
import {xyx} from 'path'
or
const {xyz} = require(path)
The braces are just restructuring the export object.
On the other hand , default exports are only one export per module , so they are pretty plain.
//exporting default
const xyz =() >{
};
export default xyz
//Importing
import xyz from 'path'
or
const xyz = require(path)
I hope this was pretty simple to understand, and by now you can understand why you import React modules within braces...
Named Export: (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.
// imports
// ex. importing a single named export
import { MyComponent } from "./MyComponent";
// ex. importing multiple named exports
import { MyComponent, MyComponent2 } from "./MyComponent";
// ex. giving a named import a different name by using "as":
import { MyComponent2 as MyNewComponent } from "./MyComponent";
// exports from ./MyComponent.js file
export const MyComponent = () => {}
export const MyComponent2 = () => {}
Import all the named exports onto an object:
// use MainComponents.MyComponent and MainComponents.MyComponent2 here
import * as MainComponents from "./MyComponent";
Default Export: (export default)
One can have only one default export per file. When we import we have to specify a name and import like:
// import
import MyDefaultComponent from "./MyDefaultExport";
// export
const MyComponent = () => {}
export default MyComponent;
Note: The naming of import is completely independent in default export and we can use any name we like.
Here's a great answer that explains default and named imports in ES6

benefits and usage of export as statement for exporting modules on javascript?

I'm learning Javascript ES6 on codecademy and there´s a lesson about named exports and export as statement. For example:
export { specialty as chefsSpecial, isVegetarian as isVeg};
My question is about what are the benefits of using export as rather than named exports like:
export let specialty = '';
or
export { specialty, isVegetarian };
So far my thought is that export as could be used for variable names standarization in projects, but haven´t come up with further advantages on makeing use of it.
also, do developers make use of this export as functionality? I´ve tried to get answers on the web but everything that's about exports and imports doesn't mention anything about export as .
Using as just allows you to define the symbol name of the export if you want it to be different than the name is locally.
So your publicly exported symbol name can be something different than you use locally.
My question is about what are the benefits of using export as rather than named exports like:
There's no particular benefit if you're happy with the internal name being the same as the exported name. If you want a different exported name, you can either rename everything locally to the new name or use export as to change just the name of the exported symbol.
So far my thought is that export as could be used for variable names standarization in projects, but haven´t come up with further advantages on makeing use of it.
I could imagine it being convenient to centralize the definition of the exported symbols in one place without forcing the definition of the functions themselves to be all in one place. If you were designing the code originally to be exported and publicly reusable, then you'd probably just name things from the beginning with an appropriate exported name and not use export as. But, if you had a body of existing code that you were now trying to put a wrapper around and for various reasons you didn't want to rename a lot of things internally, I could see some convenience to using export as.
Keep in mind that it's just a syntactical convenience. These two scenarios are functionally equivalent:
function sayGreeting() {
console.log("hi");
}
const greet = sayGreeting;
export greet;
And this:
function sayGreeting() {
console.log("hi");
}
export {sayGreeting as greet};
Or, of course you could have just written it like this in the first place:
export function greet() {
console.log("hi");
}
Personally, I agree with Geuis that import as is more useful than export as and the canonical use for import as is when you're importing two modules that both define the same symbol name (a name collision which can happen in the real world), then you can use import as to change the local symbol name to something that does not conflict with the other module you are importing.
This is the best reference article I've found on import and export syntax:
ECMAScript 6 modules: the final syntax

Override an export from another library es6

Given a javascript library (let's say supportlibrary) which has 100 named exports, I want to create my own compat-library which exports all named exports from supportlibrary but override a single named export with another.
For now, I can export all 99 named exports manually, but this would be a tedious job. I rather would have something like:
import {SupportComponent as ExcludedSupportComponent,...rest} from 'supportlibrary';
import SupportComponent from './MySupportComponent';
export {
...rest,
SupportComponent
}
Is something like this possible using es6 / tc39-stage-x functionality? or is this only possible with CommonJs?
You should be able to do
export * from 'supportlibrary';
export {default as SupportComponent} from './MySupportComponent';
to re-export all of the exports from 'supportlibrary', then export one additional named property that will take precedence over the export * version.

What is the difference between with and without curly bracket notation in export/import statements?

I'm new to ES6 and a bit confused with the way classes are exported and imported. It seems many different notations are valid but work differently.
I wrote a class like this in src/web-api.js:
class WebApi {
// ...
}
export { WebApi };
Which I import with:
import { WebApi } from './src/web-api.js'
This works fine, but before I've tried the same thing without curly brackets and it didn't work:
export WebApi; // Tells me '{' expected
import WebApi from './src/web-api.js'; // No syntax error but WebApi is undefined
Even though on the MDN documentation for export, the notation export expression; appears to be valid.
Likewise, this is how React is imported in my application file:
import React, { Component } from 'react';
Why is one class with and another one without curly brackets? In general, how can I tell when to use and not to use curly brackets?
ES6 offers many ways to manage modules through import/export. But there are basically two main strategies:
Default export/import with export default and import module from './module'
Multiple exports/imports with export and import {member} from './module' or import * as module from './module'
(Mixing both is possible but not recommended.)
Module to export/import
function foo() {
console.log('Foo');
}
function bar() {
console.log('Bar');
}
Strategy #1: Default export/import
Export (module.js)
function foo() {
console.log('Foo');
}
function bar() {
console.log('Bar');
}
export default {foo, bar};
/*
{foo, bar} is just an ES6 object literal that could be written like so:
export default {
foo: foo,
bar: bar
};
It is the legacy of the "Revealing Module pattern"...
*/
Import (main.js)
import module from './module';
module.foo(); // Foo
module.bar(); // Bar
Strategy #2: Multiple exports/imports
Export (module.js)
export function foo() {
console.log('Foo');
}
export function bar() {
console.log('Bar');
}
Import (main.js)
import {foo, bar} from './module';
foo(); // Foo
bar(); // Bar
/*
This is valid too:
import * as module from './module';
module.foo(); // Foo
module.bar(); // Bar
*/
As I said previously, ES6 modules are much more complex than that. For further information, I recommend you to read Exploring ES6 by Dr. Axel Rauschmayer, especially this chapter: http://exploringjs.com/es6/ch_modules.html.
In your case, if you import from the src/web-api.js file without the curly braces, you should have anexport default something in the src/webfile-api.js
Without curly braces
class WebApi {...};
export default WebApi;
In your file
import WebApi from './src/web-api.js'
// Here, the element withexport default in the src/web-api.js file should be imported without the curly braces anywhere.
PS: It must have only one export default for each file.
With curly braces
export { WebApi }
In your file
import {WebApi} from './src/web-api.js'
Dan Abramov explains clearly the export/import methods in ES6 at this answer.
When should I use curly braces for ES6 import?
The braces are just syntactic sugar. It will use the variable name as the object key, for example:
const a = 1;
const test = {a}; // same as {a: 1};
It can also be used to destructure the object by its variable name. It will check if the object has any properties with the same value as the variable name and then output a value if one is found:
const test = {a: 1};
const {a} = test; // a = 1
In modules, the general use case is that when you import there is usually braces since modules get imported as MODULE.function or MODULE.class. It'd be more intuitive to look at exports first:
For exporting, it's using the syntactic sugar I mentioned before - you're exporting it as an object. When you do export { WebApi }; what you're really doing is export {WebApi: WebApi}. This makes it easier to access things as you can just do 'MODULE.WebApi' now to access the class instead of having it pollute the namespace unnecessarily. This is also required for all exports!
Moving on to imports, what you're doing in the import statements is essentially destructuring the module object and picking a property to save into a variable of the same name. In your case, when you do import {WebApi} from './src/web-api.js' you'd be doing something like import WebApi = web-api.js['WebApi'] from './src/web-api.js' (this isn't valid syntax but just to show you what it's doing in the background). This is also required to properly import module functions/classes. There is also the option of importing the whole module, just as NodeJS does: import * as ModuleName from './src/module.js'. This will put all of exported functions/classes into the ModuleName object so that it can be treated as a normal module.
However, if a module has a default export, braces are not not needed for import and export. For example, react probably has export default React in its files - that's why there doesn't need to be braces around it when you do import React from 'react'
Hope I wasn't too confusing and let me know if I can clarify anything!

How to change namespace for default member import javascript

In learning more about ES6/2015 imports, I've come across a couple of cases where I'd like to change the namespace of the default member in the import scope.
Basically the equivalent of import {myMember as name} from 'my-module', but for the default member. I expected something like import defaultMember, {defaultMember as name} from 'my-module', but that seems not to work.
It seems like this should possible:
Mozilla Docs
It is also possible to use the default syntax with the ones seen above (namespace imports or named imports). In such cases, the default import will have to be declared first...
Perhaps not the actual answer, but a solution that I'm using.
For this example, I was using Node-Simple-Schema, and did not want to track imports of it as it is often used across the global scope on the project I'm working on.
The problem is that when import SimplSchema from "simpl-schema'; is used, then "SimpleSchema" as a convention is not available globally.
So I created a code file "SS2.js" and placed the following in it:
import SimpleSchema from 'simpl-schema';
var SS2 = SimpleSchema;
export {SS2};
Then in the working file, I do a subsequent "chained" import and with the following:
import {SS2} from './imports/SS2.js';
SimpleSchema = SS2;
This gives me the default module export convention "SimpleSchema" available globally once again.

Categories

Resources