Daksh Gautam's Blog

What's the fuss about ES modules ?

August 01, 2020

Hello everybody, I hope you all are doing great 😄 It’s been more than a year since I published my last blog.

What motivated me to write this blog ?

Today’s topic is about the ES modules. Modules in JS have always confused me. I don’t know if its confusion or curiosity. After going through quite a bit of blogs as well as storm of tweets by few of my favorite JS authors, the topic of ES Modules confused me further. Most of the confusions were caused because of these common expressions summarized below -

  1. “modules are not supported in browser yet 😩”
  2. “all major browsers have enabled support for ES modules, yay !”
  3. “You could not use modules in browsers directly until ES modules came in”
  4. “Modules were always present in Node Js, but not on browser”

Whats so puzzling about these remarks ? Let me give you my perspective.

  1. I have coded a lot in React. To be honest, I was first introduced to Javascript via React. So my first question was - “I am using export/import throughout my code base, but still these blog posts suggests that modules does not exist in browser. Can’t i use export/import in browser directly ?”
  2. I have been coding in Node.js for time now. So “Why do we use export/import in React & require/module.exports in Node Js. Shouldn’t they follow the same format ?”
  3. Out of curiosity once, I went ahead and coded two very simple JS files and used import/export in them. I added JS files to HTML and ran them normally and guess what happened ? NOTHING. I was like - ”What the hell, Shouldn’t that have worked?”. And to be honest with you guys, this was one of the major reasons for writing this blog. So lets start !!!!

This blog hope to answer above questions in detail. As a side note, this blog post focusses more on the browser side of modules. I will avoid going deep into Node Js, but will give you a brief idea wherever necessary. I will write a separate blog post about Modules in Node JS in future.

Lets take a brief look at what are the problems solved by modules ? and why do we need them in the first place ?

What problems does JS module solve ?

  1. Lets say we have 50 lines of JS code with us. We want to use that code. How to use it ?

    • Simple ! Write your JS inside the script tag of HTML.
    <script>
    document.getElementById("demo").innerHTML = "Hello JavaScript!";
    </script>
  2. Now your code increased from 50 lines to 200 lines , should keep writing the code in the same way ?

    • Yes, we could definitely do that, but adding JS code in HTML file will bloat it and make it huge, seemingly impossible to read. So what we do ? We copy our JS code in new index.js file and include that file in our HTML

      <script src="index.js"></script>
  3. Now, lets say you have a friend who wrote some helper code for you. How would you use it ? Lets say, that file has only one giant function with 500 lines of code. How would you use that code ?

    1. One very simple way would be to copy the 500 lines of code and put it in your index.js file. This is okay in the current case, what if all 10 of your friends have written some code for you, would you copy the code in your index.js for all 10 of your friends? You could, but it causes of a couple of problems.

      1. Your index.js will become so huge that it will become impossible to read and debug
      2. Lets say for simplicity that you overcome some problem somehow, how would you allow multiple developers contribute to code base ? They would necessary have to deal with the code they did not even write.
    2. Another way would be to create a separate file helper.js and include both index.js as well as helper.js in your HTML file in order. helper.js will set the helperFn in the global scope, and then index.js would use that function accessing the global scope.
    // index.html
    <script src="helper.js"></script>
    <script src="index.js"></script>
    
    // helper.js
    function helperFun() {
    	console.log('hello world');
    }
    window.helperFun = helperFun;
    
    // index.js
    helperFun() // 'hello world'

    Will the above code work ? Hell yes ! Does this method have any problems ? Hell Yes !

    1. Order of script is important - Lets say we reverse the order of scripts of the above example. We put index.js first and then helper.js . Then your code in index.js which uses function set by helper.js is not present. This might not seem to be a problem right now, but it is a problem when you have 50 JS files all depending on each other.
    2. Makes the code hard to maintain - A new developer joining your team have to spend decent amount of extra time to figure the chaining of scripts highlighted above.
    3. Scope pollution - Global scope is polluted. If you dont know about scopes, read this - https://scotch.io/tutorials/understanding-scope-in-javascript. Scope allows you to share code amongst scripts (one script will add the variable to the script and other would use it). This leads to couple of problems

      1. Some of the scripts have access to global variable which they dont even need in the first place. To give you example, lets say we have 3 script files - index1.js, index2.js, index3.js; index2.js uses some helper function from index1.js. Should index3.js have access to the helper function that was added by index1.js when index3.js is not even using it ?
      2. Following the example above, likewise index3.js could just come and alter the helper function added by index1.js

Although there are different patterns that try to address these problems (more about them in later posts). But they are not something that were provided by JS intrinsically. So,

How do we solve above problems ?

We would want to have a way that would allow us to achieve something usable while avoiding these problems. Could you tell what is the solution - 3…2…1, you guessed it right Modules

With ECMAScript 2015, modules were introduced. And for years to come the modern browsers like Chrome and Firefox started adding support for the module system. To this date not all browsers support this system. ****The list of browsers that support modules can be found here

I am pretty sure, some of you might have a few questions in your minds

  1. 2015 ? Seriously ? What took TC39 so long to release this feature ?
  2. Even till now, not all browsers have support for modules ? What is taking so long to implement module systems in ALL the browsers ?

Continuing from the previous discussion, a few more questions to ponder over are

  1. If you have had some experience in React with Webpack or Parcel, you have been using import/export for quite some time. What do they do under the hood?
  2. Node Js uses require/module.exports statements, but browser don’t even know what they are. Where does that come from and what is so special about them ?

Lets tackle these questions one by one. I will reorder these questions to give you a clear understanding.

  1. Node Js uses require/module.exports statements, but browser don’t even know what they are. Where does that come from and what is so special about them ?

    • The first version of Node.js 0.1.14 was launched in 2011-08-26 and modules systems were part of it. Head to this link, and do a text search for modules. This means that modules were introduced pretty early on in Node.js and has been there for the long time.
    • The loading of modules is synchronous. What does that mean ? Let me give you and example.

      // index.js
      const hello = require('hello.js');
      const helper = require('helper.js');
      console.log('hello index.js')
      ----------------------------------------------
      
      // hello.js
      module.exports = function() {
      	console.log('hello function');
      }
      ----------------------------------------------
      
      // helper.js
      module.exports = function() {
      	console.log('helper function');
      }
      ----------------------------------------------
      
      // Execute - 'node index.js'
      -------------CONSOLE_OUTPUT------------------
      'hello function'
      'helper function'
      'hello index.js'
    • Lets try to go through above code and execute execute index.js from the perspective of Node.js compiler. Node.js complier sees the first require('hello.js') statement, it goes on and fetches, loads & runs the hello.js as a module. Once that file is loaded, the Node.js compiler moves on the second require('helper.js') statement, it goes, fetches and runs helper.js as a module.
    • Now the question to ask is, can this strategy work in browsers ? Ummm…NO. Why ? Because if a module is loaded in this fashion in browser, the page would just STOP for every script to load. Hence we cannot use this approach.
  2. If you have had some experience in React with Webpack or Parcel, you have been using import/export for quite some time. What do they do ?

    • Webpack & Parcel are called module bundlers.
    • They do a very simple task - they take your modules that you created using import/export copies their source codes and pastes them SMARTLY in one single JS file.
    • There is a reason the word SMARTLY is highlighted, because this Webpack perform great optimizations before pasting your code. Few optimizations are listed below

      1. Minification - which just means as the name suggests, minifies the code of the modules. How ? A basic way would be to remove spaces in the source code, changing the large names of the class names etc.
      2. Tree Shaking - removes the unused code
  3. 2015 ? Seriously ? What took TC39 so long to release this feature ?

    1. There are many stakeholders and many different platforms involved (Node.js, npm, browsers, JS engines, TypeScript, TC39, etc.). That means, ES modules should be compatible with huge number of platforms. Lets take an example of Node.js, Node.js already has module system implemented, we dont wanna break them.
    2. As you know, getting alignment from these many stake holders takes time decent amount of times
  4. What is taking so long to implement module systems in ALL the browsers ?

    1. Different browsers/platforms use different technologies under the hood - Chrome use V8, Firefox uses SpiderMonkey. All of them have to integrate ES modules without breaking previous functionalities.

Phewww!!! That was a lot, right ? I think that’s it for the blog. I hope you might have learned one or two new things today.

Bye Bye 👋👋👋👋 See you in 2 weeks !!


© 2023, Love you Papa, Mummy & Amma. ❤️