Hussein Sarea
If you’ve spent any time working with Javascript over the past 5 or so years, you’ve probably come across Javascript Imports and Exports. Introduced as part of the ECMAScript 6 (ES6) specification in June 2015 and now supported in all major browsers, Imports and Exports have shaped the way we build modern Javascript applications. In the beginning, Javascript wasn’t designed to be modular. However, 2009 saw the release of Node JS, a popular JavaScript runtime environment that runs outside the browser. Node JS soon began supporting modularity through the CommonJS require
function and with this developers began searching for solutions to bring modularity to Javascript in the browser.
A Javascript module is a file that can export variables
, functions
, and classes
that can be imported anywhere, allowing developers to share code across multiple files or projects.
Exporting variables
, functions
, and classes
is made simple in Javascript with the export
keyword, allowing developers to specify which data/functionality they would like accessed outside the module. Take the following example of a Dice module
:
dice.js
const NUMBERS = [1, 2, 3, 4, 5, 6];
function min() {
return Math.min(...NUMBERS);
}
function max() {
return Math.max(...NUMBERS);
}
export function roll() {
return Math.floor((Math.random() * max()) + min());
}
The Dice module
exports a single function, rollDice()
, making it the only data/functionality available to be imported elsewhere. In some cases, the developer may decide that more than one piece of data/functionality, or even all data/functionality should be available outside of the Dice module
. In this case, the developer could simply prefix each variable
and function
with the export
keyword:
dice.js
export const NUMBERS = [1, 2, 3, 4, 5, 6];
export function min() {
return Math.min(...NUMBERS);
}
export function max() {
return Math.max(...NUMBERS);
}
export function roll() {
return Math.floor((Math.random() * max()) + min());
}
Now every variable
and function
in the Dice module
can be imported individually elsewhere. However, in cases where there is lots of data/functionality to be imported, importing everything individually can become frustrating. Thankfully Javascript also provides developers with the default
keyword. Using the default
keyword, developers can specify a default export
. In the Dice module
, the developer could create a default export
to make all of the module data/functionality available elsewhere with an import-defined prefix:
dice.js
export default {
NUMBERS,
min,
max,
roll
}
What and how a developer imports data/functionality from a Javascript module depends entirely on what data/functionality the developer would like to make use of and how the module exports that data/functionality. In the Dice module
, there is individually exported data/functionality and a default export
.
The syntax for importing default exports
in Javascript is simple. If the developer was to import the default export
from the Dice module
it may look like this:
dice.js
import Dice from './modules/dice.js';
Note - ./modules/dice.js
is the path to the file.
The developer could now reference the default export
as Dice, giving them access to everything in the default export
object as: Dice.NUMBERS
, Dice.min()
, Dice.max()
, Dice.roll()
.
To prevent potential name clashes, Javascript allows default exports
to be imported under any valid name. For example, the Dice module
could be imported as Foo
:
dice.js
import Foo from './modules/dice.js';
The developer could now reference the default export as Foo
,
giving the them access to everything in the default export
object as: Foo.NUMBERS
, Foo.min()
, Foo.max()
, Foo.roll()
.
In some cases a module may not provide a default export
, the default export
may not provide the data/functionality the developer wishes to use, or default export
may return lots of redundant data/functionality. For these situations, Javascript allows the developer to import individual data/functionality. To import individual data/functionality, the developer should wrap the desired exported data/functionality in curly brackets. For example, if the developer would like to import the NUMBERS
and the roll()
data/functionality from the Dice module
:
dice.js
import { NUMBERS, roll } from './modules/dice.js';
This would give the developer access to the NUMBERS
and roll()
data/functionality as NUMBERS
and roll()
, exactly the names given to them by the module they were exported from. There are times when it's not suitable to use the names given by the module the developer is importing from are not suitable. When importing from multiple modules, the chances of name clashes become increasingly likely, the given names may not be descriptive enough. For these situations, Javascript provides the as
keyword. The as
keyword can be used to rename imported individual data/functionality. For example, should the developer want to import the NUMBERS
data as DICE_NUMBERS
and the roll()
functionality as rollDice
:
dice.js
import { NUMBERS as DICE_NUMBERS, roll as rollDice } from './modules/dice.js';
This would give the developer access to the NUMBERS
and roll()
data/functionality as DICE_NUMBERS
and rollDice()
.
In some cases, the developer may wish to import all available individual data/functionality from a module, without listing out every individual export. Not all modules will export all of that data/functionality as a default export
. In these cases the developer can import all module exports under a namespace, using the *
key. For example, if the developer wished to import all the Dice module
data/functionality under the namespace Bar
:
dice.js
import * as Bar from './modules/dice.js';
This would give the developer access to the NUMBERS
, min()
, max()
, and roll()
data/functionality as Bar.DICE_NUMBERS
, Bar.min()
, Bar.max()
, and Bar.roll()
.
Javascript modularity has changed the way developers build web applications. There are several ways to import data/functionality from a module, hopefully, this post provides an insight into when and why to use each import syntax.