JavaScript ES6 import / export I am experimenting with JavaScript ES6 import and export and wanted to achieve the following:


Use the import / export style of including JavaScript files.
Import a class file and create an instance
Import a class file that extends another class
Expose functions to the index.html scope - call from an inline onclick event
Is document. available within the module scope


Is this the correct, or recommended way of doing this, or is there a better way?

index.html

&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;meta charset="UTF-8"&gt;
        &lt;title&gt;Javascript - Import and Export class examples&lt;/title&gt;
    &lt;/head&gt;
    &lt;body&gt;  
        &lt;h1&gt;Javascript Import and Export class examples&lt;/h1&gt;
        &lt;div id="myDiv"&gt;&lt;/div&gt;
        &lt;button onclick="window.updateScreen()"&gt;Do it&lt;/button&gt;
        &lt;script type="module"&gt;          
            import('./main.js').then(module =&gt; {
                /* This function is now available on the button onclick */
                window.updateScreen = module.updateScreen;              
            });
        &lt;/script&gt;
    &lt;/body&gt;
&lt;/html&gt;


main.js

import { Animal } from './animal.js';
import { Human } from './human.js';
import { Dog, Cat } from './pets.js';

const petOwner = new Human('Bob', 'Male', '21/03/19');
const pets = [ new Dog('Riley', petOwner), new Cat('Sherry', petOwner) ];

petOwner.speak();
pets[1].speak();
pets[0].speak();

function updateScreen() {
    let elem = document.getElementById('myDiv');
    elem.textContent = 'HELLO';
}

export { updateScreen };


animal.js

class Animal {
    constructor(alive, hr) {
        this.isAlive = alive;
        this.heartRate = hr;
    }
}
export { Animal };


human.js

import {Animal} from './animal.js';
class Human extends Animal {
    constructor(name, sex, dob) {
        super(true, 60);
        this.name = name;
        this.dob = dob;
        this.sex = sex;
    }   
    speak() {
        console.log('Hello');   
    }
    get age() {
        return now-dob;
    }
}
export { Human };


pets.js

import {Animal} from './animal.js';

class Cat extends Animal {
    constructor(name, owner) {
        super(true, 200);
        this.name = name;
        this.owner = owner;
    }
    speak() {
        console.log('Meow');    
    }
}

class Dog extends Animal {
    constructor(name, owner) {
        super(true, 120);
        this.name = name;
        this.owner = owner;
    }
    speak() {
        document.getElementById('myDiv').textContent = 'Woof';  
    }
}

export { Cat, Dog };
Your main.js does not have a direct dependency on Animal class, so much there is no reason to import it there.  In fact, from standpoint of minimizing browser calls to include files, it might make better sense to keep all classes in a related hierarchy together in a single file. So, I would probably have Animal, Human, Cat and Dog in the same file unless the was a compelling reason to split them - like Human was used on every page while Cat an Dog were only used sparingly in the site.

Generally, it is a good idea to be consistent about what sort of “side effects” a piece of code (class) makes.  I know this is just an example, but it seems odd for these classes to log out the results of the speak() methods, when it really is the caller that should determine what happens with the result of the method call (logging it, updating Page with it, etc.)

Main.js seems a little odd with regards to why you are exporting the updateScreen function. This function doesn’t really do anything different after the first time it is executed so not sure why it is exported as a function. Also, this module creates the side effect off all of the instantiated objects speaking upon inclusion of the module.  This would happen even if the updateScreen function was never called. Again this seems like odd behavior to be happening upon import.

In Human class, is date of birth a property specific only to humans, or should this exist on Animal class.  You should consider passing and setting a proper date object for this property.  Should age be a property on the object?  This is able to be determined in the constructor, right? It seems odd to me to have getter method for this “property” and not the others. In the age getter, there is reference to “now” variable that is undefined and you are further trying to perform subtraction using dob string value that has not been converted to proper Date object.  Use Dates here for this math.

In Dog class to have especially questionable side effect in the speak method in that you have hard coded a reference to a div to be updated.  This class should not need to understand Dom at all. The way it is now limits reuse of this class because it is tightly coupled to the Dom via this logic which has nothing to do with a dog at all.