'How to do import/export a class in Vanilla JavaScript (JS)

I'm using Vanilla JavaScript (JS) and trying to leverage the concept of class and module import/export which came as part of ECMA-2015(ECMA-6) release.

Please see the code snippet below:

rectangle.js file -

export default class  Rectangle{
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
}

myHifiRectangle.js file -

import Rectangle from 'rectangle.js';

class MyHiFiRectangle extends Rectangle {
  constructor(height, width) {
      super(height,width);
      this.foo= "bar";  
 }
}

I'm trying to use above *.js files in an HTML web page test.html as shown below:

<!DOCTYPE html>
<html lang = "en">
   <head>
      <meta charset = "UTF-8">
      <title>Javascipt by Rasik Bihari Tiwari</title>
       <script src="Scripts/rectangle.js"></script>
       <script src="Scripts/myHiFiRectangle.js"></script>
      <script type="text/javascript">
    
   var v = new MyHiFiRectangle(2,4);
   console.debug(v.foo);
      </script>
   </head>
   <body >

   </body>

</html>

Then, I tried browsing test.html file in browser. Result of browsing for of the browsers is as below:

On Google Chrome I get below error:

Uncaught SyntaxError: Unexpected token export

On Mozilla firefox I get below error:

SyntaxError: export declarations may only appear at top level of a module

SyntaxError: import declarations may only appear at top level of a module

ReferenceError: MyHiFiRectangle is not defined[Learn More]

I tried reordering the JS files which are referred in the head tag of the HTML file but it makes no difference.

P.S. I'm not using any transpilers like Babel. I'm trying to see the working of native support of 'class' and module export/import construct in JS (ECMA-2015 release aka ECMA-6) and how it works.



Solution 1:[1]

I went through this and I've a solution with a third js file as module. rectangle.js will be same and myHifiRectangle.js file have only one modification.

import Rectangle from './rectangle.js';

export default class MyHiFiRectangle extends Rectangle {
      constructor(height, width) {
      super(height,width);
      this.foo= "bar";  
   }
}

Now, we need a third file which will be a module file, let say, script.js

import MyHiFiRectangle from './myHifiRectangle.js'

var v = new MyHiFiRectangle(2,4);
console.log(v.foo);

Now, the third file, script.js should be made a module. More on modules here. I have all three files under modelJS folder.

<script type="module" src="/modelJS/script.js"></script>

Now, when you run, you should see 'bar' getting printed in the developer tool's console tab.

Solution 2:[2]

I'd like to show you an alternative solution to what @Andy Gaskell has posted.

Firstly, you need babel in order to be sure that you can use ES6 in your browser. This is to ensure that your code will still work as some browsers (legacy ones like IE) does not support modern javascript (ES6 and beyond) features such as import/export and classes.

You can add the following script

`<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script>`

before any other javascript files mentioned above.

Secondly, if you include your javascript classes inline, the scope of those classes become global, even if they reside in their own physical js files.

I've included working example below, I've changed it a little bit so that it would work in the code snippet. You want to replace the script with the script that contains your javascript file like you have done in your code.

<!DOCTYPE html>
<html lang = "en">
   <head>
      <meta charset = "UTF-8">
      <title>Javascipt by Rasik Bihari Tiwari</title>
       <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script>

      <!-- Replace them with script with src pointing to your javascript -->       
      <script type="text/javascript"> 
        class  Rectangle{
          constructor(height, width) {
            this.height = height;
            this.width = width;
          }
        }

        class MyHiFiRectangle extends Rectangle {
          constructor(height, width) {
              super(height,width);
              this.foo= "bar";  
         }
        }
           
       var v = new MyHiFiRectangle(2,4);
       console.log(v.foo);
       </script>
   </head>
   <body >

   </body>

</html>

UPDATED

ok. cool! Btw, if I bring all the class definition into the script tag of my html page itself then I don't even need to reference babel-core in the head tag. Why would it be required?

You might need it for browsers that does not support classes like IE. But, if compatibility for legacy browsers are not in your requirement, then you don't need it.

...do I even need the export-import stuff? What would be the significance of module export in a native javascript when every class is more or less global?

Indeed you won't need the export-import stuff since your classes are global. You only use this if you want to use the module system. If you don't use import/export your classes should be global and therefore should work. But in case that it didn't somehow. You make sure that it exists globally by attaching it to window object like so:

 window.myClass = class MyClass { /* Class definition */ }

Solution 3:[3]

For the sake of completeness, I'm adding an answer after getting hint from @curiou.netter's answer. I'm pointing out the exact errors in sequential manner in my original code files. I was able to achieve it without having additional script.js file:

  1. while referring to JS modules our script type should be module instead. I was referring to myHiFiRectancle.js like a regular JS file using srctag as src="Scripts/myHiFiRectangle.js". I also imported MyHiFiRectangle module. Here is how the head tag now looks in my test.html file after fixing this error:

    <head>
      <meta charset = "UTF-8">
      <title>Javascipt by Rasik Bihari Tiwari</title>
      <script type="module">
         import MyHiFiRectangle from './scripts/myHiFirectangle.js';
         var v = new MyHiFiRectangle(2,4);
         console.debug(v.foo);
      </script>
       </head>
    
  2. export defaultstatement was missing in myHiFiRectangle.js file. Every class has to be exported as a module to become useful.myHiFiRectangle.js file looks like below after fixing this error:

    import Rectangle from './rectangle.js';
    export default class MyHiFiRectangle extends Rectangle {
      constructor(height, width) {
      super(height,width);
      this.foo= "bar";  
     }
    }
    
  3. My script files had another error in the way I was referring to modules.

    Wrong way:

    import Rectangle from 'rectangle.js';
    import MyHiFiRectangle from '/scripts/myHiFirectangle.js';
    

It causes below error which is self explanatory:

Uncaught TypeError: Failed to resolve module specifier "rectangle.js". Relative references must start with either "/", "./", or "../".

Correct way:

    import Rectangle from './rectangle.js';
    import MyHiFiRectangle from './scripts/myHiFirectangle.js';

Solution 4:[4]

In most browsers this is enabled via feature flag.

Chrome: go to about:flags and enable "Experimental Web Platform features".

Firefox starting with version 54: dom.moduleScripts.enabled.

Edge 15 or newer: enable "Experimental JavaScript Features" in about:flags.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 RBT
Solution 2
Solution 3
Solution 4