0
\$\begingroup\$

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

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

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 };
\$\endgroup\$
1
\$\begingroup\$

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.

\$\endgroup\$
  • \$\begingroup\$ Thanks Mike, great suggestions. updateScreen is just an example of what I may need one day, but I'll have a tidy up based on your comments. \$\endgroup\$ – Matthew Page Jan 20 at 14:57
0
\$\begingroup\$

Looks good to me for the most part. Some things I noticed:

  • You can export a function/class directly, e.g.

    export class Foo {}
    

    is a shorthand for

    class Foo {}
    export { Foo }
    

    The same holds for functions and variable declarations. What you. prefer comes down to preference.

  • Since import() is asynchronous, your onclick on the button will throw an error if you click the button before the Import is finished (e.g. when the network is slow or something). So you should check if window.updateScreen is already defined and/or adapt the UX (e.g. deactivate the button until the import is finished)

  • Instead of doing a dynamic import, you could bind the onclick in main.js similar to how you did with the div.

\$\endgroup\$
  • \$\begingroup\$ Thanks inyono, I kind of like the export at the end as a pure style thing. Good tips. \$\endgroup\$ – Matthew Page Jan 20 at 12:12
  • \$\begingroup\$ Glad I could help :) \$\endgroup\$ – inyono Jan 20 at 13:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.