Primitives, functions, objects, and classes defined within a module are visible to the outside only if exported. JavaScript offers several options for exporting; choose the simplest option that meets your needs in a given situation.
You may declare a reference or a class and at the same time export it—that is, inline the export keyword in the declaration. This approach is the easiest and least noisy approach to exporting. To inline an export, prefix a declaration with the export keyword, which makes the declared reference available outside the module. The name of the declared reference becomes the name that is exported—this is called a named export.
For example, the following code exports a primitive, a function, an object, and a class, with their respective names. The primitive referenced by the variable FREEZINGPOINT_IN_F is not exported and is visible only within the module/file.
| export const FREEZING_POINT = 0; |
| |
| export function f2c(fahrenheit) { |
| return (fahrenheit - 32) / 1.8; |
| } |
| |
| export const temperaturePoints = { freezing: 0, boiling: 100 }; |
| |
| export class Thermostat { |
| constructor() { |
| //...initialization sequence |
| } |
| } |
| |
| const FREEZINGPOINT_IN_F = 32; |
Marking references with export at the point of declaration makes the code transparent—we can readily see if a reference has been exported or not. However, since the exports are spread out in the file, it may make it hard to quickly get a glimpse of everything exported from a module. This is generally not a huge concern if the files are relatively small.
You’re not forced to export at the point of declaration. You can export an existing function, object, and so forth at any time by explicitly declaring an export. Although inlining exports is less noisy and avoids an extra line for export, explicitly exporting is necessary when we want to export select names when using multiple declarations.
In the following code, we explicitly export the c2f function, which we could have exported inline as well. However, we want to export only FREEZINGPOINT_IN_K, from the multiple declarations, and not export BOILINGPOINT_IN_K. Explicit export is a good choice for this.
| function c2f(celsius) { |
| return celsius * 1.8 + 32; |
| } |
| |
| const FREEZINGPOINT_IN_K = 273.15, BOILINGPOINT_IN_K = 373.15; |
| |
| export { c2f, FREEZINGPOINT_IN_K }; |
Prefer inline exports over explicit exports, and in general, use explicit exports only when an inline export is not suitable.
So far we’ve exported the declarations with their name. However, we can export them with a different name if we desire. This is useful, for example, if we decide to give a more descriptive name for a function for the outside world to use but keep a shorter name for internal use.
Here’s an example to illustrate exporting with a different name:
| function c2k(celsius) { |
| return celsius + 273.15; |
| } |
| |
| export { c2k as celsiusToKelvin }; |
The name c2k is only visible within the module—kind of like nicknames used within families. However, outside the module this function is known by the name celsiusToKelvin and can be called only with that name.
A default export may signify a major, the most significant, or the only export a module may like to expose. Although a module can have zero or more named exports, it can have at most one default export. Use a default export if you have only one reference or class to export. Even when you have multiple things to export, you may use a default export if most users of your module will use a particular reference more frequently than other exported references.
To mark an export as default, follow the export keyword with the default keyword. Default exports have one restriction, however; export default is not permitted in front of the keywords const and let while export without default is allowed. In short, inlining of default exports is permitted for functions and classes, but not for variables and constants. You may explicitly export variables and constants as default. Let’s export as default a function from our module.
| export default function unitsOfMeasures() { |
| return ['Celsius', 'Delisle scale', 'Fahrenheit', 'Kelvin', /*...*/]; |
| } |
The unitsOfMeasures() function is exported as default. Instead of exporting a function as default, we can similarly export a class as default.
In the previous code we used an inline default export on the function. We can also perform an explicit default export at a later time after declaring the function with no export, like so: export default unitsOfMeasures. As we discussed before, this approach is especially useful to export a variable that was declared using const or let; that’s because default can’t be followed by const or let.
When we create a named export, the name of the reference used in the file becomes the exported name. When we create a named export with a different name, that new name we provide becomes the name visible outside the module. When we create a default export, however, the name visible outside the module becomes default and the importing module may bind the name default to whatever name it likes. We’ll see this later when we explore imports.
If you mark an export as default when declaring a function or a class, the name you provide for the function or class is not visible outside the module. If you have no use for that name internally in your module, then you can omit the name entirely. So, for instance, instead of exporting a function like this:
| export default function unitsOfMeasures() { |
| return ['Celsius', 'Delisle scale', 'Fahrenheit', 'Kelvin', /*...*/]; |
| } |
if you have no use for that function within your module, then you may omit the name of the function, like so:
| export default function() { |
| return ['Celsius', 'Delisle scale', 'Fahrenheit', 'Kelvin', /*...*/]; |
| } |
Likewise, we can omit the name of a class, if we like, when creating an inline default export for a class. For example, export default class { /*...*/ } will work instead of export default class SomeNameNoOneCares { /*...*/ } as well.
You can reexport modules to reduce the number of imports and to make dependencies transitive. Occasionally you may want to make available to the users of your module some references that are contained in other modules.
For example, let’s say we create a weather module but we want to expose functions from the temperature module we created previously and also another hypothetical pressure module. The weather module may itself expose some references that it contains. Now suppose a module wants to use the references in the weather, temperature, and pressure modules. That user does not need three imports. Instead, by importing the weather module, thanks to reexport, the user will have access to references exported by all the three modules: weather, temperature, and pressure.
Here’s a way to reexport all exported references, except default, from temperature:
| export * from './temperature'; |
Now, the modules that import weather can use all references exported by temperature as if they were exported by weather.
You can export select references from other modules instead of reexporting everything that’s exported. For example, the following reexport will export only Thermostat and celsiusToKelvin from temperature.
| export { Thermostat, celsiusToKelvin } from './temperature'; |
An importer of the module that does this reexport will not have access to references like f2c in temperature unless it directly imports the temperature module.
Reexport all—that is, export *—does not reexport a default export. A module can reexport any reference, or the default, from another module as its own default if it chooses. Likewise, it may rename a reference from another module while exporting. This can be achieved using the facility to export with a different name we saw in Exporting with a Different Name. Here’s an example with export...as to reexport.
| export { Thermostat as Thermo, default as default } from './temperature'; |
Here the weather module is reexporting the Thermostat class from temperature with a new name, Thermo. The user of the reexporting module will know Thermostat of temperature as Thermo. Likewise, it takes the default export of temperature—that is, the function unitsOfMeasures()—and exports as its own default.
Instead of reexporting another modules default as your own module’s default, you can use any exported reference from another module as your default, like so:
| export { Thermostat as Thermo, f2c as default } from './temperature'; |
Here the function f2c, which is a named export and not a default export in temperature, is reexported from this module as its own default. In the same vein, your module can reexport the default from another module with a different name:
| export { Thermostat as Thermo, default as uom } from './temperature'; |
The function unitsOfMeasures exported as default from temperature is now reexported as uom. This technique, renaming a default from another module, is useful when a module reexports multiple modules and wants to make the defaults from more than one module available to the user.