Check out a free preview of the full Professional JS: Features You Need to Know course
The "ES Modules" Lesson is part of the full, Professional JS: Features You Need to Know course featured in this preview video. Here's what you'd learn in this lesson:
Maximiliano provides a recap of ES6 features, including the class syntax, block code variable definitions, ES modules, arrow functions, promises, and more. He also discusses how modules work as isolated containers, how to import and export items from modules, and how to enable modules in different platforms such as the browser and Node.js.
Transcript from the "ES Modules" Lesson
[00:00:00]
>> Maximiliano Firtman: So let's make a recap of ES6 again the version that most of you may be already comfortable with, but these were the basic ideas. So it was one of the major upgrades for the language since the beginning of the language. I would argue it's safe to use it on every browser today, I11, out of the equation.
[00:00:22]
It included the class syntax for OOP, so to create classes using the class keyword, blocks code variable definitions, let and const mainly, EF modules, okay? I will do a quick recap in a sec of ES modules, arrow functions, promises, something that we will cover later today, and many more features.
[00:00:44]
Some small features, some other advanced features such as generators or regular expression additions that were actually the first time that people coming from Java or C#, were not complaining so much about JavaScript, okay? On ES5, when they realized there was no way to create a class with the class syntax, they were really like annoyed like, what the hell is this, okay?
[00:01:11]
Well with ES6, okay, we have class, okay, class extends, okay? So, we will leave some advance ES6 topics for later, organized by topics, okay, but I will cover now the quick recap of modules and the basic stuff so we can set that as a base for what's new after ES6.
[00:01:32]
That's probably the most important for today's workshop. So let's talk about modules. It's a standardized way to organize and reuse JavaScript code across different files. Because before modules, we were using different files in the browser at least, we were using different files, but everything was kind of a mess in a global context.
[00:01:54]
And now we can import, you can use import and export statements for better modularity and maintainability in fact, it's a design pattern to create. There is a design pattern of creating modules for different things, it can be per topic, modules for section, modules per architecture patterns, and so on.
[00:02:18]
So instead of creating one large JavaScript file, or a lot of JavaScript files that are all going to the same bag of variables, objects, and functions, now we can create modules. So for example, typically on classic HTML, if you have two JavaScripts, JavaScripts in your HTML you can access every variable and every function of every script from any other script, even from the main HTML.
[00:02:48]
So if you have some JavaScript here, you can actually read and access all the functions defined on every script, and also from script 1 to script 2 and vice versa, okay? Everything was going to the same bag. So, they use the same global context, and one script can include or load other scripts that was another problem, okay?
[00:03:11]
From one script, you cannot specify dependencies I'm talking about before ES6, I'm talking about standard classic browser JavaScript development. The worker is an exception, and I'm not sure if you have played with web workers or service workers. We do have an import scripts, function that was added for the worker spec only.
[00:03:33]
So we cannot modularize behavior or data and now JS, had to implement its own pattern known as the common JS pattern to emulate modules. The require function and then module.exports on the other side was a way to emulate modules before they were available in ECMAScript. So that's why If you have two scripts, you can call from a script 1 print user, even if it was defined on script 2 on another file.
[00:04:08]
And you can also use variables defined in the other scripts which may be good sometimes, but when you have a large code base, and you have a team of developers working on the same project, it's a mess, right? So we don't want that. So that's why we have now ES modules, that, I mean, they are from ES6, but they have been improved over the versions as small improvements, we will see those improvements later.
[00:04:37]
So, in this case, every module work as a container isolated from the global object. The global object is different per platform so for example, if you're doing client side application for the browser is a Window option. If you're doing workers, Web Workers, Service Workers is the self option, if you're using null JS, is the global option.
[00:05:02]
Well, in this case, a module has its own separate, isolated context. So for now, ES modules are today living together with CommonJS, so you can match a mix if you want between CommonJS and ES modules. Each module works in a separate file in the browser it's normal ".js" file, for Node.js, it's the ".mjs" by default, you can change that.
[00:05:40]
And there is the global scope, it doesn't matter if it's no JS or the browser or the service worker, creates a module import tree as soon, and it's parsing JavaScript, and the modules, I will show you that in a second. So, that means that it will import, by default on ECMAScript 6, all the files statically at that moment.
[00:06:06]
When it's parsing the project, it says okay, I'm loading all the modules, all the JS files that I need. A module can export items, variables, functions, class declarations, objects, values, anything you wanna export, and also it can have one default export import, that you define in a file.
[00:06:30]
So you can export as many as you want, or one that's gonna be the default one. And any module can import other modules items, totally or partially, so I will show you some examples. And you should check on every platform, how to enable modules because remember ECMAScript is always backwards compatible and before ES6, we didn't have modules.
[00:06:58]
So that means that by default, even on modern ECMAScript, these days modules should be disabled by default for backward compatibility, unless you say somehow, I wanna use modules okay? So for example, in the window context for standard client side applications, we use type module in the scripting. That will load app.js using modules, and then it can create an import module tree from there.
[00:07:33]
But if you don't add the type module, you cannot import or export within app.js. In a worker, when you create a worker, you need to specify explicitly that, that worker will use modules because for backward compatibility by default, we don't have modules. When you create a service worker, when you register a service worker, you also specify type module.
[00:08:01]
For node.js, there are three ways to use modules in node.js. One is to just use the .mjs extension, that I know that some of you may dislike, because sounds weird, right? That mjs extension sounds weird, it was a big discussion in a null community. So but they say hey, for compatibility reasons, we cannot just leave it with .js, but if you want to use .js, you can say so by adding a flag when you're running your script, or by adding a metadata in your package .JSON.
[00:08:39]
You will say, hey, you know what in the whole project I wanna use modules, this is not a ES5 project. So this is ES6 with modules project, so then it's going to take your .JS files as modules by default. So with modules then, now the HTML can include the Script 1, and Script 1 by itself, can also say, hey, I wanna import Script 2.
[00:09:09]
And now, if you have a variable or a function in Script 2, it's not available by default anywhere else. It's only available in the isolated Script 2 context, unless Script 2 is exporting that. In this case, so Script 1 can import that variable or that function, which makes our code cleaner, okay?
[00:09:36]
And when you have a large code base, this is pretty good. So that means that now the same example, as we saw before, is not actually working. You cannot access print user or the user variable from Script 1, because it's in a different module. Unless we export those references from a Script 2, and then we import those references in Script 1, okay?
[00:10:12]
Make sense? Pretty straightforward, okay? And also, you can use default export. So instead of exporting each file individually or each component in Individually, you can do a default export and in this case I'm exporting actually, I'm not sure if you recognize this syntax. If not, it's something that we will see in a second because it's also an ES6 addition.
[00:10:36]
It has to do with object addition, this is actually JSON, doesn't look like actually it's not really JSON compatible. It's an object syntax, that is creating a new object with a property user that has the value user, with a property print user that has the value print user.
[00:10:57]
So it's an object that then I'm receiving that object from the other side, and I can set a name for that object, and then I can use an API, same Services.printUser, yeah.
>> Student: I see in Script 1, it's import capitalist services and I normally think of that as class.
[00:11:16]
>> Maximiliano Firtman: Yeah, that's a design pattern let's say, a guideline.
>> Student: Okay.
>> Maximiliano Firtman: That sometimes, but actually there are different ways to do this. And in the community there, I don't think for this particular case, there is no single answer. If you should use title case or lower case for the object, but it's been used a lot via title case.
[00:11:40]
When you are importing an object, you treat it like a select an API, like if it's a module. So it's like a module name, think about that it's an object, okay? It's an object, so it should be lowercase from that point of view, so it's an object. But it's the object that has everything from other module.
[00:11:59]
So it's kind of our module entry point that's why a lot of developers, and I kind of like that idea, are capitalizing the name of that option, but it's up to you, or up to your team, up to your guideline, okay? Make sense?
>> Student: The default import, default exports syntax doesn't enforce pass scouts.
[00:12:20]
>> Maximiliano Firtman: No, in transcript, there is no informants' enforcement instead of naming. So you can name that dollar sign if you want. So you can say import dollar sign from Script 2 or underscore because those are value identifiers, makes sense? When importing from module, this is important if you're coming from React, from Angular, if you're using Babel.
[00:12:44]
When importing from modules, the path must start with http or https, /, ./, or ../, so if not, it's not gonna work. And you need to remember to think in URLs, not in files. Which means, or example, you always need to say, when you're importing here, for example, okay, I'm saying ./, and I'm saying .JS.
[00:13:16]
If you're using Babel, because it's a transpiler maybe you're not used to add the .JS is not gonna work in the browser, because the browser works with URLs. The browser, if you don't add .js, the browser will make an HTTP request to the server through a Script 2 without .js and probably that's a 404.
[00:13:41]
Does it make sense? So when you're using ES modules, and you are not using a transpiler, that can add some data sheets to your code. You always need to work with public URLs, which means you shouldn't use short hands or ways to shorten the URL, such as not adding the file extension.
[00:14:05]
Because it's no gonna work.
Learn Straight from the Experts Who Shape the Modern Web
- In-depth Courses
- Industry Leading Experts
- Learning Paths
- Live Interactive Workshops