Require vs Import Javascript
Did you see a lot of code with imports and exports but also other ways with require and module.exports? In this video you will learn about all ways of creating modules and why do you need it at all.
So the first question. Do we have a problem at all? If we take a simple javascript file and load it in browser than all variables that we will create in root level of our file will become global. So it's fine to create varibles in functions because they will be isolated there but not just like this.
var a = "a";
because in this case we are polluting global namespace. And you might say well it's not a problem it's our project. The problem is that if something is global anybody can change it accidentally and everything is broken.
The first way of solving this was immediately invoked function expressions. As I already said we don't care about functions because all varibles are isolated inside that. We can use this approach by wrapping our whole code in a self execute function.
(() => {
var a = "a";
console.log("a", a);
})();
So this is a arrow function without a name which we call directly. The only important thing is additional round brackets which we need to be able to call this function.
If we check now in browser we don't pollute a global namespace. Everything is isolated inside this function.
But the problem is now how we organize things if we have 10 of 20 files. Sure we can wrap every file with self executed function but how we can comfortably use a function from one file in other file. It becomes complex and not comfortable quite fast.
At some point CommonJS modules were created. What is the idea? Each file is own isolated module out of the box and we can specify what we want to export from it to use in other modules and each module can import what it needs.
What is also important is that it allows us to define the entities in different modules with the same name. Because they are fully isolated are even if we export the same names it's not coming to global namespace.
In nodeJS until now we have exactly this approach.
Let's check on the example.
foo.js
const getFullName = (name, surname) => {
return name + " " + surname;
};
module.exports = getFullName;
bar.js
const getFullName = require("./foo");
console.log("getFullName", getFullName("moster", "lessons"));
If we write in console
node bar.js
We will get our full name.
So some important things here. Both bar and foo are fully isolated. When we want to use something from other module we need to require it. The only way to get something from other module is if that module (in our case foo) exports it. For this we must define it in module exports.
What you might ask at this point: "What do we do if we need to export several things?". This is completely valid and possible.
const getFullName = (name, surname) => {
return name + " " + surname;
};
const getSurname = (name, surname) => {
return surname;
};
module.exports = {
getSurname,
getFullName,
};
We can just export an object will whatever we need to export. Or we can write it with exports notation.
foo.js
exports.getFullName = getFullName;
exports.getSurname = getSurname;
bar.js
const foo = require("./foo");
console.log("foo", foo.getFullName("monster", "lessons"));
As you can see we are requiring a full module here as an object and get all things that we exported at an object.
The super important thing to remember is that require is just a function. Which means we can do with it whatever we want. For example put inside functions or conditions.
if (1 == 1) {
const foo = require("./foo");
console.log("foo", foo.getFullName("monster", "lessons"));
}
So this is just a function and this module will be required only if condition is true.
One more nice thing is to use destructuring to get directly local variables instead of a module.
const { getFullName } = require("./foo");
console.log("foo", getFullName("monster", "lessons"));
So here we directly get a local function getFullName without a module. It's quite handy.
So where you can find CommonJS modules? In all nodeJS applications by default if it was not made more complex with babel or other things that can give you other type of modular system. Also in some frontend projects depending how they were done but much rarely.
The next thing that came after common js module are es6 modules. They have the similar approaches but solve problems a bit differently. Actually all new browsers support es6 modules.
bares6.js
import getFullName from "./fooes6.js";
console.log("foo", getFullName("monster", "lessons"));
fooes6.js
const getFullName = (name, surname) => {
return name + " " + surname;
};
const getSurname = (name, surname) => {
return surname;
};
export default getFullName;
So I copied 2 files and change them a bit. Instead of require we used import syntax. To make a single export instead of module.exports
we have export default
.
Now we need to specify in the script that we want load it as a es6 module.
<script src="./bares6.js" type="module"></script>
So here we added type=module for the import. But here is a problem. I just opened in browser my index.html like a file. And it won't work with module. We get an error
Access to script at 'file:///Users/oleksandrkocherhin/projects/my/monsterlessonsacademy/bares6.js' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, chrome-untrusted, https.
To make everything working we must serve an index.html at localhost instead. We can do it quite easy with npm package which is called serve.
npm install -g serve
serve
As you can see now we can just to localhost:5000 and see the same page. And in this case it works and we can see out console.log.
If we want to export several things from a module we can use export word.
export const getFullName = (name, surname) => {
return name + " " + surname;
};
export const getSurname = (name, surname) => {
return surname;
};
And in the same way with destructuring we can get the same exported functions.
import {getFullName} from "./fooes6.js";
console.log("foo", getFullName("monster", "lessons"));
So this is how use are using the newest module system. export or export default and import.
Now the most important question what is the difference between commonJS modules and es6 modules. Why es6 modules were invented and are they better.
The main difference is that es6 modules are static and commonJS modules are dynamic. Which means you can nest imports of es6 modules. You can't put them inside functions or conditions. They are allowed only in root of the file. It's because require works in runtime and imports in parsed time. Obviously in parse time parser can't know if our statement is true or false. But this is a benefit. Es6 modules throw errors in parsed time not in runtime so much earlier.
Also all imports are hoisted. This means that they are moved to the top of the file. Exactly like var properties works. Which means they are available for the whole file at the beginning. Require on the other hand is only available after it was required.
We can import only from string literal. This means that we can't somehow calculate what module we import or write anything else except of string on the right. But it's all possible with require where we can calculate what we are importing and provide there whatever we want.
And the last question that might bother you "Why then we still have CommonJS in Node if es6 is newer and better?". Because NodeJS is huge and complex and it's not that easy to migrate everything to the new module system.
Just to remember. The biggest benefit of es6 modules compared to CommonJS is that they are static. This is why you know exactly what you imports. This is why it's better for tooling and static analysis of the code.
So this what everything that you need to know about modules in javascript world.
Want to conquer your next JavaScript interview? Download my FREE PDF - Pass Your JS Interview with Confidence and start preparing for success today!
📚 Source code of what we've done