Access variables declared in dynamically imported JavaScript?
Asked Answered
G

1

2

I am working on Single Page Architecture (SPA) Web application. I am have implemented functionality to dynamically load different pages of application.

This is what my spa.js looks like

var spa = {
    init: async () => {

        await import('./page.js')
        spa.data['page'].apply()
    },

    register: (page, data) => {
        spa.data[page] = data
    },

    data: {},
}

window.onload = () => {
    spa.init()
}

And this is what my page.js looks like

const pageData = {
    apply: () => {
        document.body.innerHTML = getMSG()
    }
}

function getMSG() {
    return "hello message!"
}

spa.register('page', pageData)

The working process explained:

  • Initially spa.js loads. spa.data is empty
  • It initiates spa.init(), which loads page.js
  • page.js registers its data to spa variable
  • Now control jumps back at spa.js and from spa.data for page, it will execute apply()

This works fine. But here's a strange thing!

The apply() method has access to bare getMSG(), but when I try to execute getMSG() it via browser's developer console I get ReferenceError implying that getMSG() is not defined at all! Same goes with our pageData

Here is the hosted heroku app, you can try - dyn-import

Although it is very good as I don't have to worry about clashing functions in different files, I want to know how this is happening.

Can someone explain how can I access methods and variables defined in page.js. I am aware about exports, but it is somehow possible without exports!

Gaucherie answered 19/5, 2020 at 13:19 Comment(3)
What do you mean by executing with console? you want it to be globally accessible?Voltage
await import('./page.js').then() is very strange. Either you await something(), or you go Promise-style with something().then(). But not bothSilva
@ThakurKarthik By console, I mean browser console (F12, developer console). When I try to access the method via console, it says undefined. But the script can access it, as if it's global. My question is is it global or not? If yes, why it doesn't work in console, if not how can apply method access it?Gaucherie
B
1

You cannot directly access functions and variables defined in the module if they are not exported.

Reason why spa variable is accessible globally is because spa.js is normal script, referenced in the HTML document head.

<head>
    ...
    <script src="./spa.js"></script>
</head>

If you, on the other hand, import script, variables are in different scope, unless you export them. So page.js is treated as a module.

You could, of course, attach function getMSG() to the global spa variable, or pageData, but both would be workarounds.

spa.getMSG = getMSG;

You can read more about differences between modules and standard scripts here - MDN JavaScript modules.

Brownout answered 19/5, 2020 at 13:35 Comment(5)
But the call to apply() function in spa.init() can access getMSG(), which is part of module and not exported.Gaucherie
Yes, because you attached pageData to spa manually using spa.register('page', pageData). Do similar for the function. For example: spa.getMSG = getMSG.Brownout
Nenad, even if I don't attach getMSG to spa it fulfils my requirements. My question is more about why it works in spa without attaching, and why it doesn't in console?Gaucherie
I already told you everything. You can think of it as private function inside of page.js. It works in spa because you wrapped the function in pageData.apply and then you attached pageData to spa with spa.register('page', pageData). Not sure what is not clear to you.Brownout
Thanks! It's clear now. It would be really helpful if you could link to some docs related to scoping of imported modules in your answer aboveGaucherie

© 2022 - 2024 — McMap. All rights reserved.