48

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

Please see the code snippet below:

rectangle.js:

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

myHifiRectangle.js:

import Rectangle from 'rectangle.js';

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

I'm trying to refer above mentioned JS files in an HTML page named test.html (Refer code snippet):

<!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 loading test.html in browser. The result is different on different browsers.

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 had no impact.

Note: To be clear again, I'm not using any transpilers like Babel. I'm trying to check the native support of export/import class and module constructs in Vanilla JS and how it works.

2
  • I have posted an answer to your question. Commented Jan 16, 2019 at 22:23
  • 1
    Remove <script src="Scripts/rectangle.js"></script> and replace <script src="Scripts/myHiFiRectangle.js"></script> with <script src="Scripts/myHiFiRectangle.js" type="module"></script>. That fixes one problem. The other is that myHifiRectangle.js does not create a global variable (which you should stop using anyway). To get that, add window.MyHiFiRectangle = MyHiFiRectangle;at the end of myHifiRectangle.js. Commented Jan 16, 2019 at 22:25

4 Answers 4

41

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Great! I wasn't aware of this module attribute. May be Chrome hadn't implemented it at the time of writing this question. Btw, I was able to achieve it using module attribute in my HTML file itself as mentioned my own answer. I didn't require a separate script.js files to call my modules. I really appreciate you for putting effort on my old question. I learned something new today.
6

I'm also adding an answer after getting hint from curiou.netter's answer in this thread.

I will point out the errors in precise sequential manner in the original code files. By the way, I was able to fix the issue without involving additional script.js file:

  1. While referring to JS modules, the script type should be module instead. I was referring to myHiFiRectancle.js like a regular JS file using src tag as:

    src="Scripts/myHiFiRectangle.js"
    

    I also imported MyHiFiRectangle module. Here is how the head tag now looks in the 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 default statement was missing in myHiFiRectangle.js file. Every class has to be exported as a module when it has to be used from a different place. The rectified myHiFiRectangle.js file looks like below:

    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 importing modules.

    Incorrect 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';
    

1 Comment

I had my file extension named .mjs which broke the import. So while keeping type="module" i switchted the file extension to .js and it works now.
3

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 */ }

5 Comments

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? class construct is now natively supported in javascript beginning ECMA-6. I should not need a transpiler while working with native javascript code. Transpilers as is do not play any role but they come in action when they are invoked by some module bundler like webpack or browserify.
Now the question which remains is that, all this doesn't work when I move my classes to individual separate js files. The thing which seems to be bursting at the moment is export and import happening in individual js files. But, if I have included all the js files (containing the class files) in sequential/required fashion (the order in which they are referenced) on the page where I'm using them then 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?
Ok. So as per the module system (assuming we are talking on a day when current browsers have implemented the export/import thingy) - The moment I use the export statement for a class residing in a js file of its own then it suddenly becomes non-global by default and will be available to only those script files which import it explicitly using import keyword available in ECMA6 specification. Is that a fair statement to make?
Yep. I believe that is accurate. You can see that this is true when you try to import a file where export ... does not exists (in your js app). The file that tries to import the said module will complain. This is due to the module system. The scope of the code is preserved within a file, unless they are imported/exported.
For the sake of completeness, can you please add a note in your answer that export/import keywords don't work as of today as they are still in implementation phase (pertaining to ECMA-6 specifications) by various browser vendors. My code snippet will also work just if I remove export import statements from them as all classes are global by default even if they reside in their own physical *.js files.
1

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.

2 Comments

I can see url for access chrome flags is chrome://flags/
I tried it after enabling the hidden feature flags which you've recommended both for Chrome and Firefox but still they give same error output in developer tools console tab when I browse my web page containing class construct in referred js files.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.