Why the JavaScript Import Statement is So Confusing
Joe Eames
Posted on May 26, 2020
If you’re anything like me, you’ve occasionally (or frequently) been confused by the import statement in JavaScript. This is a relatively new (in computer years it’s older than I am) statement that was added to JavaScript to standardize and officially support importing items from other files.
But still today, I sometimes look at the syntax of the import statement and do a double take. Just when I start getting used to what the curly braces are doing, suddenly there’s an asterisk, or even nothing at all. Why are there so many variations going on? And yes, destructuring is a great thing, but its syntax with the curly braces sometimes gives me a headache.
So let’s do a quick breakdown of the import statement and look at each possible variation, and what it’s doing.
The first thing to understand is that the purpose of the import statement is to bring something from one JavaScript file into another. This can be a function, a class, an object, or really anything else in JavaScript (except perhaps self actualization. You’ll have to look elsewhere for that).
Looking at the following highly technical diagram that I spent hours creating, we can see the relationship between two files and how the import statement helps one file import things from another file.
Let’s glance at the syntax for that
import utility from './utilities';
utility();
This may look fairly simple, but it’s not. It might already be confusing you. The problem is that when you learn this just by looking at what other people are doing you may assume something that isn’t true. Here we import utility from utilities. So you MIGHT think that means that the utilities file contains something called utility and that we are asking for that. And you may also naturally assume that the name utility is important. Neither statement is true. The identifier utility is something that gets created right there in the import statement. And it can be any name. For example the following is just as valid with no changes to the utilities file.
import monkey from './utilities';
monkeys();
Here I’ve named it monkeys instead of utility. That’s just as valid. This code will do exactly the same thing as the previous code. I decided on the name when I wrote the import statement. I named it monkeys. There is no correlation between this name, and whatever is in the utilities file.
There IS a correlation between HOW I import, or the syntax I’m using here, and what is in the utilities file. In this case, this is the import syntax for when the utilities file has a default export. Let’s glance at the utilities file and see what it contains.
export default function dogs() {
console.log('woof woof')
}
First off we see that the names in our utilities file have no correlation to the names in our code file where we are importing. First we took this dogs function and imported it under the name utility. Then we imported it under the name monkeys.
Now generally you do want to have a correlation. Usually whatever the names are in the source file are useful because they’re descriptive. Renaming the dogs function to monkeys is probably not a good practice for readability.
Now let’s look at that default statement. The export statement lets us export something, the default keyword indicates that this is the default export. There can be only one. So if this is what is in the utilities file, then that syntax we saw earlier is how we import this.
So already we see that import can be a bit misleading if we assume things just by looking at it.
Let’s look at the next variation of import, and the most common one used.
import { cows, cats } from './utilities';
cows();
cats();
Here I’m exporting two different things from the utilities file. A cows function and a cats function. Ok, so there are two possible confusing parts of this import. First, the syntax. This is the object destructuring syntax in JavaScript. We won’t go into it here, just suffice to say that it’s cool and if you’re not very used to it, it’s easily confusing.
The other possibly confusing thing is that now, the names we use DO have a correlation to what is in our import file. Using this syntax, the names need to match. I can’t import the monkeys function as cats like this (there’s a way to do that we’ll see in a moment). I have to use the same name. Exactly the same name. Let’s see what’s in our utilities file that makes this work.
export function cows() {
console.log('moo');
}
export function cats() {
console.log('meow');
}
Here we’re exporting two functions, one named cows, the other cats. The names do have to match. So don’t get confused by the fact that when importing in the first way, using a default export, the name doesn’t have to match, and in the other way using the object destructuring syntax, the names do have to match. Keep that straight.
Now let’s see how to rename those identifiers as we import them:
import { cows as moo, cats as meow } from './utilities';
moo();
meow();
We can simply use the as keyword and then we can choose our own name. Most of the time people don’t do that. But it is possible.
Now let’s look at the next variation of import, which is again easily confusing.
import * as animals from './utilities';
animals.cows();
animals.cats();
Here we’re grouping up everything that is exported into a single object. Then we can use that object to access anything that was exported. We do need to know the names of the things that got exported so that we can call them. And we don’t get to rename the pieces, but we really don’t need to since they’re all gathered together. That name I created, animals, can be anything. That's created only here in the import statement. It has no correlation to any name in the file we're importing from. This just creates an object (in our case named animals) that contains all the exports from the file we're importing.
One note, if there’s a default export, then it shows up as a member of that object named default.
animals.default();
The final syntax I want to look at that again confused me the first time I saw it is this.
import './mysteryfile'
I had finally started getting used to importing using the object destructuring syntax and then I saw this and I was like….I just can’t even. I wondered what was going on here? What is the point? What’s happening with this file?
Well, the answer is that we use this syntax when we want to import a file just for the side effects. For example css files, or javascript files that create global objects (it’s an older habit but still in use today) etc. So if you see this then you know that something is happening, but you can’t always be 100% sure of what.
And there you have it. That’s the import statement. There are some variations here, but that’s the main uses you’ll encounter. I hope that helps. Hit me up on twitter if you have any questions.
Happy Coding!
Looking to Skill up in Angular?
Come to my free webinar: Data Binding in Angular: Explained
Enjoy this blog? Sign up for my newsletter to get more articles like this before they're published here. here.
Visit Us: thinkster.io | Facebook: @gothinkster | Twitter: @gothinkster
Posted on May 26, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
November 28, 2024