Zum Inhalt springen
aviral gupta

// I3.1 · ca. 34 Min. · Aufbau

import und export: ein Programm in Module aufteilen

Nach dieser Lektion teilen Sie ein Programm auf mehrere Dateien auf: Jede Datei exportiert, was andere nutzen dürfen, importiert, was sie braucht, und hält alles andere privat.

Lektion 1 von 5 in I3 Module, Node.js und npm

Anfang des Moduls

Danach können Sie

  • Features aus einer Datei exportieren und in einer anderen mit einem relativen Pfad samt Endung namentlich importieren
  • Modul-Scope und Live-Bindungen erklären: was eine importierende Datei sehen, lesen und nicht neu zuweisen kann
  • Vorhersagen, wann der Code eines Moduls läuft: einmal, vor dem eigenen Code der importierenden Datei
  1. Aufwärmen · Aufgabe 1 von 7

    Zum Aufwärmen aus Lektion B4.4 zum Scope. Was gibt dieser Code aus?

    {
      const secret = 42;
    }
    console.log(typeof secret);
  2. Vorhersagen · Aufgabe 2 von 7

    Sagen Sie es vorher, bevor Sie weiterlesen. Zwei Dateien importieren counter.js. Wie oft erscheint „counter.js runs“?

    // counter.js
    console.log("counter.js runs");
    export let count = 0;
    
    // logger.js
    import {count} from "./counter.js";
    export const start = count;
    
    // main.js
    import {count} from "./counter.js";
    import {start} from "./logger.js";
    console.log("main runs");
  3. Üben · Aufgabe 3 von 7

    Ergänzen Sie das Schlüsselwort, mit dem main.js VAT aus prices.js importieren kann.

    ____ const VAT = 0.19;
    const VAT = 0.19;
  4. Üben · Aufgabe 4 von 7

    Ordnen Sie jedem Stück Modul-Syntax zu, was es tut.

  5. Üben · Aufgabe 5 von 7

    Was gibt main.js aus?

    // math.js
    export function add(a, b) {
      return a + b;
    }
    export function multiply(a, b) {
      return a * b;
    }
    
    // main.js
    import {add, multiply} from "./math.js";
    console.log(multiply(add(1, 2), 4));
  6. Denksport · Aufgabe 6 von 7

    Knobelaufgabe. Der Import steht nach dem console.log. In welcher Reihenfolge erscheinen die Zeilen?

    // setup.js
    console.log("setup");
    
    // main.js
    console.log("main");
    import "./setup.js";
  7. Anwenden · Aufgabe 7 von 7

    Kleine Aufgabe: Teilen Sie ein kleines Shop-Skript in drei Module auf. prices.js hält den Mehrwertsteuersatz privat und exportiert gross(net). cart.js importiert gross, hält die Artikel privat und exportiert addItem(name, net) und total(). main.js fügt Tee für 4 und Kuchen für 6 hinzu und gibt die Summe mit Mehrwertsteuer aus. Führen Sie es mit Node.js in dem Ordner aus, der eine package.json mit "type": "module" hat.

    Prüfen Sie Ihr Ergebnis anhand dieser Liste

Selbst programmieren

Lesen Sie das ausgearbeitete Beispiel und lösen Sie dann die Übungen. Ihr Code läuft in Ihrem Browser oder auf Ihrem Computer und wird nie hochgeladen.

Ausgearbeitetes Beispiel

Drei Module und ein gemeinsamer Zähler

prices.js exportiert withVat und currency und hält VAT privat. counter.js exportiert count und increment und gibt aus, wenn es läuft. logger.js importiert counter.js ebenfalls. main.js importiert aus allen dreien. Die Ausgabe zeigt, dass counter.js einmal lief, dass main.js VAT nicht sehen kann, dass count eine Live-Bindung ist, die beide Aufrufe von increment() geändert haben, und dass main.js ihr nichts zuweisen kann.

main.js

import {withVat, currency} from "./prices.js";
import {count, increment} from "./counter.js";
import {log} from "./logger.js"; // logger.js imports counter.js too

console.log(withVat(10), currency);
console.log(typeof VAT); // VAT belongs to prices.js

increment();
log("hello");
console.log("count:", count); // a live binding: 2

try {
  count = 10;
} catch (error) {
  console.log(error.name + ":", error.message);
}

prices.js

const VAT = 0.19; // not exported: private to this file

export function withVat(net) {
  return Math.round(net * (1 + VAT) * 100) / 100;
}

export const currency = "EUR";

counter.js

console.log("counter.js runs");

export let count = 0;

export function increment() {
  count += 1;
}

logger.js

import {increment} from "./counter.js";

export function log(message) {
  increment();
  console.log("log:", message);
}

Ausführen mit

node main.js

Ausgabe

counter.js runs
11.9 EUR
undefined
log: hello
count: 2
TypeError: Assignment to constant variable.
  • counter.js runs wurde zuerst und nur einmal ausgegeben: Importe laufen vor dem Code von main.js, und der zweite Import, aus logger.js, verwendete das Modul wieder.
  • count ist 2, obwohl main.js es nie zugewiesen hat: increment() änderte die Variable in counter.js, und der Import ist eine Live-Ansicht darauf.
  • count = 10 schlug fehl: Ein Importeur kann eine importierte Bindung lesen, aber nie neu zuweisen.
Ändern und ausführen

Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.

Beim ersten Ausführen lädt Ihr Browser den JavaScript-Runner herunter (bis zu 0.1 MB) und speichert ihn im Cache. Ihr Code läuft in der eigenen Engine Ihres Browsers und bleibt auf Ihrem Gerät.

Übungen

Übung 1 von 2

Nur die öffentlichen Funktionen exportieren

main.js ist ein Modul, das andere Dateien importieren werden. Es soll genau zwei Funktionen exportieren: slugify(title), das "Hello World!" in "hello-world" verwandelt, und isSlug(text). Die Hilfsfunktion clean(text) wird nur innerhalb der Datei benutzt und muss privat bleiben. Der Startcode exportiert auch clean. Entfernen Sie diesen Export, sodass die öffentliche API des Moduls nur aus den beiden Funktionen besteht.

Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.

Beim ersten Ausführen lädt Ihr Browser den JavaScript-Runner herunter (bis zu 0.1 MB) und speichert ihn im Cache. Ihr Code läuft in der eigenen Engine Ihres Browsers und bleibt auf Ihrem Gerät.

Hinweise
  1. Hinweis 1

    Führen Sie die Tests aus: Das Modul exportiert drei Namen. Welcher ist nur ein Helfer?

  2. Hinweis 2

    Eine Funktion ohne export ist innerhalb ihrer eigenen Datei trotzdem nutzbar.

  3. Hinweis 3

    Löschen Sie das Wort export vor function clean.

Eine Lösung zeigen

Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.

function clean(text) {
  return text.toLowerCase().replace(/[^a-z0-9 ]/g, "").trim();
}

export function slugify(title) {
  return clean(title).split(/ +/).join("-");
}

export function isSlug(text) {
  return /^[a-z0-9]+(-[a-z0-9]+)*$/.test(text);
}
Auf dem eigenen Computer ausführen

Installieren Sie ECMAScript 2026 oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.

main.js

export function clean(text) {
  return text.toLowerCase().replace(/[^a-z0-9 ]/g, "").trim();
}

export function slugify(title) {
  return clean(title).split(/ +/).join("-");
}

export function isSlug(text) {
  return /^[a-z0-9]+(-[a-z0-9]+)*$/.test(text);
}

main.test.js

import {test} from 'node:test';
import assert from 'node:assert/strict';
import * as slugs from './main.js';

test('Das Modul exportiert genau slugify und isSlug', () => {
  const names = Object.keys(slugs).sort();
  assert.deepEqual(names, ['isSlug', 'slugify'], `main.js exportiert ${JSON.stringify(names)}`);
});

test('slugify macht aus einem Titel einen Slug', () => {
  const got = slugs.slugify('Hello  World!');
  assert.equal(got, 'hello-world', `slugify gab ${got}`);
});

test('isSlug akzeptiert einen Slug und lehnt einen Titel ab', () => {
  assert.equal(slugs.isSlug('hello-world'), true, 'hello-world ist ein Slug');
  assert.equal(slugs.isSlug('Hello World'), false, 'Hello World ist kein Slug');
});

package.json

{
  "type": "module"
}

package.json sagt Node.js, dass die .js-Dateien Module sind; lassen Sie die Datei im Ordner.

Programm ausführen:

node main.js

Prüfungen ausführen (learnrun.js muss im selben Ordner liegen):

node --test
learnrun.js herunterladen

Übung 2 von 2

Eine Live-Anzahl der Artikel

main.js ist ein kleines Speichermodul. Es soll addItem(name) und count exportieren, die Anzahl der bisherigen Artikel, sodass ein Importeur count nach jedem addItem sich ändern sieht. Der Startcode exportiert count als items.length, einmal berechnet, als das Modul lief, es bleibt also 0. Machen Sie count zu einem let, das addItem aktualisiert.

Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.

Beim ersten Ausführen lädt Ihr Browser den JavaScript-Runner herunter (bis zu 0.1 MB) und speichert ihn im Cache. Ihr Code läuft in der eigenen Engine Ihres Browsers und bleibt auf Ihrem Gerät.

Hinweise
  1. Hinweis 1

    Führen Sie die Tests aus: count bleibt 0. Wann wird items.length im Startcode gelesen?

  2. Hinweis 2

    Ein importiertes let ist eine Live-Bindung: Weist main.js einen neuen Wert zu, sehen ihn die Importeure.

  3. Hinweis 3

    export let count = 0; und in addItem: count = items.length;

Eine Lösung zeigen

Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.

const items = [];
export let count = 0;

export function addItem(name) {
  items.push(name);
  count = items.length;
}
Auf dem eigenen Computer ausführen

Installieren Sie ECMAScript 2026 oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.

main.js

const items = [];

export function addItem(name) {
  items.push(name);
}

export const count = items.length;

main.test.js

import {test} from 'node:test';
import assert from 'node:assert/strict';
import {addItem, count} from './main.js';
import * as store from './main.js';

test('count beginnt bei 0', () => {
  assert.equal(store.count, 0, `count ist ${store.count}, bevor es einen Artikel gibt`);
});

test('Das importierte count folgt addItem', () => {
  addItem('tea');
  addItem('cake');
  assert.equal(count, 2, `nach zwei Artikeln ist das importierte count ${count}`);
});

package.json

{
  "type": "module"
}

package.json sagt Node.js, dass die .js-Dateien Module sind; lassen Sie die Datei im Ordner.

Programm ausführen:

node main.js

Prüfungen ausführen (learnrun.js muss im selben Ordner liegen):

node --test
learnrun.js herunterladen

Häufige Fehler

Einen Namen importieren, den das Modul nicht exportiert

import {sum} from "./math.js";
console.log(sum(1, 2));

Was Node.js ausgibt

SyntaxError: The requested module './math.js' does not provide an export named 'sum'

Warum, und die Lösung

Die Namen in den geschweiften Klammern müssen genau die exportierten Namen sein. math.js exportiert add, nicht sum, das Modul lässt sich also nicht verknüpfen, und nichts läuft. Importieren Sie add, oder benennen Sie beim Import um: import {add as sum} from "./math.js". Der Fehler ist ein SyntaxError, obwohl die Syntax stimmt: Er tritt beim Verknüpfen der Module auf, bevor irgendein Code läuft.

Einer importierten Bindung etwas zuweisen

import {count} from "./counter.js";
count = count + 1;
console.log(count);

Was Node.js ausgibt

TypeError: Assignment to constant variable.

Warum, und die Lösung

Ein Import ist in der importierenden Datei nur lesbar, auch wenn das exportierende Modul ihn mit let deklariert hat. Nur counter.js kann count ändern. Exportieren Sie eine Funktion, die das tut, etwa export function increment() { count += 1; }, und rufen Sie diese auf.

Die Dateiendung weglassen

import {add} from "./math";
console.log(add(1, 2));

Was Node.js ausgibt

Error [ERR_MODULE_NOT_FOUND]: Cannot find module

Warum, und die Lösung

Node.js probiert bei einem ES-Modul-Pfad keine Endungen aus: "./math" sucht eine Datei, die genau math heißt und nicht existiert. Ein Browser würde die URL ./math anfordern und genauso scheitern. Schreiben Sie den vollen Dateinamen: "./math.js".

JavaScript im Browser: die eigene Engine Ihres Browsers in einem abgeschotteten Worker. Syntaxfehler findet acorn 8.18.0, MIT. Lizenz und Quellcode

Abschlussquiz

5 Fragen, ohne Hinweise. Ab 80 % ist die Lektion abgeschlossen.

Erledigen Sie zuerst alle Aufgaben oben, um das Abschlussquiz freizuschalten.

Problem melden

Etwas ist falsch oder unklar? Beschreiben Sie es kurz, dann wird es geprüft und korrigiert.

#

Mindestens 20 Zeichen.

Nur, wenn Sie eine Antwort wünschen.

Kernideen

Eine Datei, ein Modul

In einem Modul hat jede Datei ihren eigenen Scope auf oberster Ebene. Eine const oder Funktion dort ist privat für diese Datei, es sei denn, Sie setzen export davor: export function add(a, b) { … }. Eine andere Datei holt sie mit import {add} from "./math.js". Die Namen in den geschweiften Klammern müssen mit den exportierten Namen übereinstimmen. Der Pfad beginnt mit ./ oder ../ und enthält die Endung .js: Browser und Node.js raten sie nicht. export funktioniert nur auf oberster Ebene einer Datei, nie in einer Funktion, und Module laufen automatisch im Strict Mode.

Importe sind Live-Bindungen, nur lesbar

Ein Import ist keine Kopie. Er ist eine Live-Bindung an die Variable des exportierenden Moduls: Ändert dieses Modul den Wert, etwa mit count += 1 in seinem eigenen increment(), sieht jeder Importeur den neuen Wert. Der Importeur kann ihr nichts zuweisen: count = 10 in main.js wirft TypeError: Assignment to constant variable. Ein importiertes Objekt lässt sich über seine Eigenschaften trotzdem ändern, und jeder Importeur sieht diese Änderung ebenfalls, weil alle dasselbe Objekt teilen.

Ein Modul läuft einmal

Beim ersten Import eines Moduls läuft sein Code auf oberster Ebene. Jeder spätere Import, aus jeder Datei, bekommt dasselbe, bereits ausgewertete Modul, seine Variablen sind also geteilter Zustand, keine frischen Kopien. Import-Deklarationen werden hochgezogen (Hoisting): Die importierten Module laufen vor dem eigenen Code der importierenden Datei, egal in welcher Zeile der Import steht. Ein Modul, das nur laufen soll, etwa Einrichtungscode, lässt sich wegen seiner Nebenwirkungen importieren: import "./setup.js";.

Quellen

Zuletzt geprüft am 5. Oktober 2026