Samuel fiedler: In „normalem“ JavaScript auf Objekte aus Modulen zugreifen

Hallo an alle!

Ich habe mich heute gefragt, ob ich auf von einem Modul exportierte Klassen in normalem JavaScript zugreifen kann. Beispiel:

<script type="module">
  export default function test() {
    return 42;
  }
</script>
<script>
  console.log(test());
</script>

Das funktioniert leider nicht. Gibt es da irgendeine Möglichkeit, die Funktion test verfügbar zu machen?

Au revoir,
Samuel Fiedler

--
In CSS gibt es ja position: absolute; und position: relative;. Was ist nun die Mischung daraus?
Ganz klar: position: resolute!

akzeptierte Antworten

  1. Hallo Samuel,

    Du kannst auf Exporte von Modul A nur zugreifen, wenn Modul B das Modul A importiert.

    Dass das bei inline-Scripten geht, scheint mir zweifelhaft, das habe ich noch nicht gesehen und finde auch spontan keine Syntax dafür.

    Das hier geht jedenfalls nicht:

    <script type="module">
    include { blub } from "#test";
    
    blub();
    </script>
    
    <script id="test" type="module">
    export function blub() {
       console.log("Plitsch Platsch");
    }
    </script>
    

    Nach meiner Kenntnis muss alles, was Du importierst, von extern kommen.

    Rolf

    --
    sumpsi - posui - obstruxi
    1. Hallo Rolf B!

      Nach meiner Kenntnis muss alles, was Du importierst, von extern kommen.

      OK. Ich habe das Gleiche auch schon befürchtet.
      Mir fiel aber gerade eben noch ein, dass man doch in einem Modul einen Event-Listener an ein Element hängen könnte. Kann man irgendwie auf die Event-Listener zugreifen, sodass man diese Funktionen hat?

      Au revoir,
      Samuel Fiedler

      --
      In CSS gibt es ja position: absolute; und position: relative;. Was ist nun die Mischung daraus?
      Ganz klar: position: resolute!
      1. Hallo Samuel fiedler!

        Ich habe jetzt doch eine Möglichkeit gefunden:

        <!DOCTYPE html>
        <script type="module">
          function test() {
            console.log("Hello World!");
            return 0;
          }
          window.addEventListener('DOMContentLoaded', function() {
            window.test = test;
          });
        </script>
        
        <script>
          function delay(n) {
            return new Promise(function(resolve) {
              setTimeout(resolve, n * 1000);
            });
          }
          window.addEventListener('DOMContentLoaded', async function() {
            await delay(0.001);
            window.test();
          });
        </script>
        

        Man muss also einen Event Listener irgendwo dranhängen, der die Funktion in ein beliebiges globales Objekt stellt. Nachdem das getan ist, funzt es.

        Au revoir,
        Samuel Fiedler

        --
        In CSS gibt es ja position: absolute; und position: relative;. Was ist nun die Mischung daraus?
        Ganz klar: position: resolute!
        1. Hallo Samuel,

          ja, so was ähnliches habe ich gerade auch zusammengefrickelt. Wenn man aus einem Modul heraus etwas globales bereitstellen will, muss man es an ein globales Objekt anpappen. Das kann das window-Objekt sein, es könnte auch eine vertrauliche private Eigenschaft an einem HTML Element sein.

          Was Du gemacht hast, ist eine zeitliche Abfolge zu erzwingen, über die Millisekundenpause. Wenn das ECMAScript-Modul intern ist, ist das machbar. Kommt es von extern, dürfte das nicht reichen. Jegliche fixe Zeitspanne kann nur falsch sein.

          Lass uns dafür das Timing von Modulen anschauen.

          • Normale inline-Scripte werden in dem Moment ausgeführt, wo der Browser sie antrifft. D.h. das HTML Parsing wird unterbrochen, das Script läuft und dann geht das Parsing weiger. Das muss so, weil ein solches Script mit document.write HTML generieren darf.
          • ECMAScript-Module laufen automatisch mit dem defer-Attribut. Das heißt, dass sie genau dann ausgeführt werden, wenn das HTML Dokument fertig geparsed ist. Erst nachdem alle defer-Scripte gelaufen sind, wird DOMContentLoaded gefeuert.
          • Ein ECMAScript-Modul, das DOMContentLoaded nicht verzögern soll, kann das async-Attribut bekommen, dann läuft es frühestens bei Ausführung der defer-Queue und spätestens, wenn es verfügbar ist.

          Heißt also:

          <script type="module">
             window.test = function() { alert("test"); };
          </script>
          
          <script>
             test();
          </script>
          

          kann deshalb nicht funktionieren, weil das Modul deferred läuft und die Zuweisung an window.test erst erfolgt, wenn der zweite Scriptblock schon durch ist. Man könnte auf die Idee kommen, dem ECMAScript-Modul eine ID zu geben und sich auf das load-Event des Scriptelements zu registrieren, um zu wissen, wann das Modul da ist - das hilft aber nichts, weil load bei einem inline-Script nicht gefeuert wird. Und ein afterscriptexecute-Event gibt's nicht - nicht offiziell, das ist Firefox-spezial.

          Das hier

          <script type="module">
             window.test = function() { alert("test"); };
          </script>
          
          <script>
             document.addEventListener("DOMContentLoaded", function() {
                test();
             });
          </script>
          

          würde dagegen funktionieren, weil DOMContentLoaded erst nach Verarbeitung aller defer-Scripte läuft.

          Aber ich mach's trotzdem rot. Ein ECMAScript-Modul, das vorsätzlich Dinge in den globalen Scope ballert, ist nicht im Sinne des Erfinders.

          Die sinnvolle Lösung wäre, das ganze Projekt als ECMAScript-Modulbaum zu schreiben, mit vielen kleinen überschaubaren Modülchen, und diese für den Produktivbetrieb zu einer Datei zu bündeln (bspw. mit Webpack).

          Rolf

          --
          sumpsi - posui - obstruxi