- Lernen
- Informatik & Informationswissenschaften
- Programmierung
- JavaScript – DOM, Ereignisse und asynchroner Code
- Klassen und Module in JavaScript
Klassen und Module in JavaScript mit Entwicklerwerkzeugen
package.json und nutze die alltäglichen Werkzeuge eines modernen Arbeitsablaufs: einen Linter und Codeformatierer, einen Entwicklungsserver und Bundler sowie einen Testrunner. Zum Abschluss siehst du, wo TypeScript und Frontend-Frameworks in diese Werkzeugkette passen.AnfängerEinheit 9 von 9 in Kurs: JavaScript – DOM, Ereignisse und asynchroner Code
Was du lernst
- Erstelle mit Klassen Objekte mit gemeinsamem Verhalten, schütze ihre Daten, baue Hierarchien durch Vererbung auf und erkläre die Prototypenkette, auf der Klassen aufbauen.
- Definiere eine Klasse und erstelle Objekte mit newSchreibe eine Klasse mit Konstruktor, Feldern, Methoden, Gettern und Settern, erstelle Instanzen mit
newund sorge dafür, dassthisauf das richtige Objekt verweist. - Schütze Daten mit privaten Mitgliedern und nutze Werte mit statischen Mitgliedern gemeinsamVerbirg den internen Zustand eines Objekts hinter privaten Feldern und Methoden und lege Werte und Hilfsfunktionen, die zur Klasse als Ganzes gehören, in statischen Mitgliedern ab.
- Baue Klassenhierarchien mit extends und super aufErstelle Unterklassen, die Verhalten erben und überschreiben, rufe die Oberklasse mit
superauf, prüfe Typen mitinstanceofund entscheide, wann Vererbung oder Komposition besser passt. - Erkläre die Prototypen hinter Objekten und KlassenBeschreibe, wie JavaScript Eigenschaften entlang der Prototypenkette findet, wie Klassen und
extendsdiese Kette aufbauen und warum eingebaute Prototypen unverändert bleiben.
- Definiere eine Klasse und erstelle Objekte mit new
- Teile ein Programm in Module auf, die Funktionen exportieren und importieren, führe sie in Browsern und Node.js aus, lade Code bei Bedarf und gib jedem Modul eine klare Aufgabe.
- Exportiere und importiere Code zwischen DateienTeile ein Programm in ES-Module auf und verbinde sie durch benannte und Standardexporte, umbenannte Importe und Namensraumimporte sowie Reexporte. Verstehe dabei, wie sich Modulgültigkeitsbereich und Live-Bindungen verhalten.
- Führe Module im Browser und in Node.js ausLade Module auf einer Webseite mit
type="module"von einem lokalen Server, löse Modulpfade und bloße Namen auf und verstehe, wie Node.js zwischen ES-Modulen und CommonJS wählt. - Lade Module bei Bedarf und gliedere ein Projekt in ModuleNutze dynamisches
import()und Top-Level-await, teile eine Anwendung in Module mit klaren Aufgaben auf und vermeide Probleme mit Seiteneffekten und zyklischen Importen.
- Exportiere und importiere Code zwischen Dateien
- Beschreibe ein Projekt in
package.json, führe seine Aufgaben mit npm-Skripten aus und installiere, aktualisiere und wähle Abhängigkeiten so aus, dass jeder Computer dieselbe funktionierende Zusammenstellung von Paketen erhält.- Richte ein Projekt mit npm und package.json einErstelle ein Projekt mit
npm init, lies und bearbeite die wichtigsten Felder vonpackage.json, führe Aufgaben mit npm-Skripten undnpxaus und lies semantische Versionsnummern. - Installiere, aktualisiere und wähle Abhängigkeiten ausInstalliere Pakete als Laufzeit- oder Entwicklungsabhängigkeiten, lies Versionsbereiche, nutze
package-lock.jsonundnpm cifür reproduzierbare Installationen, halte Abhängigkeiten aktuell und wähle Pakete sicher aus.
- Richte ein Projekt mit npm und package.json ein
- Nutze einen Linter und einen Codeformatierer, um Probleme zu erkennen und einen einheitlichen Stil beizubehalten, einen Entwicklungsserver und Bundler, um ein Projekt auszuführen und auszuliefern, und einen Testrunner, um die Funktionsfähigkeit des Codes zu prüfen.
- Erkenne Probleme mit einem Linter und formatiere Code automatischNutze ESLint, um wahrscheinliche Fehler zu finden, ohne den Code auszuführen, und Prettier für ein einheitliches Layout. Konfiguriere beide und führe sie über Skripte und den Editor aus.
- Stelle ein Projekt mit einem Entwicklungsserver bereit und bündele es mit einem BundlerErkläre, was Bundler und Entwicklungsserver tun, durchlaufe den Zyklus aus Entwickeln, Bauen und Vorschau mit Vite und verstehe Tree Shaking, Minifizierung, Dateinamen mit Hash, Source Maps und Umgebungsvariablen.
- Prüfe Code automatisch mit einem TestrunnerSchreibe Unittests mit Assertions für Funktionen und Klassen, führe sie mit dem eingebauten Node.js-Testrunner oder Vitest aus, lies Fehlermeldungen und entwirf Code, der sich leicht testen lässt.
- Erkenne Probleme mit einem Linter und formatiere Code automatisch
- Erkläre, was TypeScript und Frontend-Frameworks zu JavaScript hinzufügen, wie sie Module, npm und Buildwerkzeuge nutzen und wie alle Werkzeuge in einem Projekt zusammenarbeiten.
- Erkläre, was TypeScript zu JavaScript hinzufügtBeschreibe, wie statische Typen in TypeScript Fehler vor der Ausführung erkennen, wie TypeScript-Code zu JavaScript wird und wann sich der zusätzliche Schritt lohnt.
- Ordne Frameworks in eine vollständige moderne Werkzeugkette einErkläre, was Frontend-Frameworks tun und wie sie auf Modulen, npm und Bundlern aufbauen, und füge die Werkzeuge dieser Einheit zu einem alltäglichen Arbeitsablauf zusammen.
- Erkläre, was TypeScript zu JavaScript hinzufügt
Klassen und Module in JavaScript einfach erklärt
1Erstelle Objekte mit gemeinsamem Verhalten
Ein einfaches Objekt gruppiert zusammengehörige Werte. Eine Klasse ergänzt eine wiederholbare Möglichkeit, Objekte mit eigenen Daten und gemeinsamem Verhalten zu erstellen. Beginne mit Konstruktoren und Methoden und steuere dann Änderungen über private Felder und eine öffentliche Schnittstelle. Vererbung ermöglicht verwandten Klassen, dieses Verhalten zu spezialisieren. Die Prototypenkette erklärt, wie JavaScript die Methoden findet und warum eine Instanz andernorts gespeicherten Code nutzen kann.
1.1 Erstelle und nutze Klasseninstanzen
Eine Klasse beschreibt Objekte mit derselben Struktur und demselben Verhalten. Jedes daraus erstellte Objekt ist eine Instanz. Verwende eine Klasse, wenn mehrere Objekte dieselben Operationen benötigen; für einen einzelnen Einstellungswert reicht oft ein einfaches Objekt.
Gib jedem Objekt eigene Daten
Klassennamen verwenden üblicherweise PascalCase, etwa Rectangle. Der constructor läuft jedes Mal, wenn new eine Instanz erstellt. Darin verweist this auf das neue Objekt. Eine Zuweisung an this.width speichert eine Instanzeigenschaft; ein Parameter namens width allein ist nur eine lokale Variable.
class Rectangle {
constructor(width, height) {
this.width = width; // cm
this.height = height;
}
area() {
return this.width * this.height;
}
perimeter() {
return 2 * (this.width + this.height);
}
}
const a = new Rectangle(3, 4);
const b = new Rectangle(5, 2);
a.area(); // 12
a.perimeter(); // 14
b.area(); // 10
Bei a.area() ruft JavaScript die Methode auf und setzt this auf a. Die Breite von 3 cm multipliziert mit der Höhe von 4 cm ergibt 12 cm². Für den Umfang werden die beiden unterschiedlichen Seiten addiert und diese Summe verdoppelt. Das ergibt 14 cm. Eine Änderung von b.width ändert nur b.
Methoden haben kein Schlüsselwort function und keine Kommas zwischen sich. Eine Klasse hat höchstens einen Konstruktor. Wird er bei einer Klasse ohne extends weggelassen, erhält die Klasse einen leeren Standardkonstruktor. new gibt die erstellte Instanz automatisch zurück.
Ein Feld deklariert eine Eigenschaft im Klassenrumpf: unit = "cm"; gibt jeder Instanz einen eigenen Wert. label; hat zunächst den Wert undefined. In einer Klasse ohne extends werden Instanzfelder initialisiert, bevor der Konstruktorrumpf ausgeführt wird.
Über Zugriffsmethoden lesen und zuweisen
Ein Getter wird ausgeführt, wenn eine Eigenschaft gelesen wird. Mit get area() { return this.width * this.height; } liest du a.area ohne Klammern. Das ist eine Alternative zur obigen Methode, die a.area() verwendet.
Ein Setter wird bei einer Zuweisung ausgeführt und kann ungültige Eingaben ablehnen. Er benötigt einen separaten Speicherplatz: set width(value) { this.width = value; } ruft sich wiederholt selbst auf, bis der Stapel überläuft. Speichere den Wert unter einem anderen Namen, etwa im privaten Feld #width.
Das Empfängerobjekt beim Übergeben einer Methode beibehalten
class Counter {
count = 0;
increment() { this.count++; }
}
const counter = new Counter();
const inc = counter.increment;
// inc(); // TypeError: this is undefined
const safeInc = () => counter.increment();
safeInc(); // counter.count is 1
Klassenmethoden laufen im strikten Modus. Ein losgelöster Aufruf hat kein Empfängerobjekt vor dem Punkt, daher ist this gleich undefined. Auch der Aufrufer eines Callbacks kann ein anderes Empfängerobjekt übergeben. Ein Pfeilfunktions-Wrapper erhält den Aufruf auf counter; counter.increment.bind(counter) erstellt eine gebundene Funktion. Ein Pfeilfunktionsfeld, increment = () => { this.count++; };, erfasst ebenfalls das this der Instanz.
Der Aufruf von Rectangle(3, 4) ohne new löst einen TypeError aus. Die Verwendung einer Klasse vor der Initialisierung ihrer Deklaration löst einen ReferenceError aus. Klassen unterscheiden sich hier von Funktionsdeklarationen, die früher in einer Datei aufgerufen werden können.
JSON erhält Daten, nicht das Verhalten von Klassen. JSON.stringify(a) speichert die eigenen öffentlichen Werte für Breite und Höhe. Das Parsen dieses Textes erstellt ein einfaches Objekt; erstelle mit new Rectangle(data.width, data.height) wieder eine Instanz, um ihre Methoden zu nutzen.
Wichtige Erkenntnisse
newerstellt eine Instanz und führt ihren Konstruktor aus;thisbezeichnet das Empfängerobjekt eines Methodenaufrufs.- Felder enthalten die Daten jeder Instanz; gewöhnliche Methoden werden gemeinsam genutzt.
- Getter werden wie Eigenschaften gelesen; Setter laufen bei einer Zuweisung und benötigen separaten Speicher.
- Verwende einen Pfeilfunktions-Wrapper oder eine gebundene Funktion, wenn ein Callback sein Empfängerobjekt behalten muss.
- JSON stellt beim Parsen gespeicherter Daten keine Klassenmethoden wieder her.
1.2 Schütze den Zustand mit privaten Feldern
Ein Objekt kann eine kleine öffentliche Schnittstelle anbieten und gleichzeitig seine gespeicherten Daten privat halten. Das ist Kapselung. Die Schnittstelle entscheidet, welche Änderungen gültig sind; der private Zugriffsschutz allein prüft keinen Wert, der an einen öffentlichen Setter übergeben wird.
Validierung an der Schnittstelle platzieren
Ein privates Feld beginnt mit # und muss im Klassenrumpf deklariert werden. Code innerhalb dieser Klasse liest es über this.#target. Code außerhalb kann t.#target weder lesen noch schreiben: Das ist ein Syntaxfehler und kein Eigenschaftszugriff, der undefined zurückgibt.
class Thermostat {
static MIN = 10;
static MAX = 30;
#target = 20;
get target() { return this.#target; }
set target(value) {
if (!Number.isFinite(value) ||
value < Thermostat.MIN ||
value > Thermostat.MAX) {
throw new RangeError("Target must be between 10 and 30 °C");
}
this.#target = value;
}
}
const t = new Thermostat();
t.target = 22;
t.target; // 22
// t.target = 35; // throws RangeError
Der Getter ermöglicht das Lesen des Werts, ohne den Speicher offenzulegen. Der Setter lehnt zuerst nicht endliche oder nicht numerische Eingaben ab, dann Werte außerhalb des Bereichs von 10 bis einschließlich 30 °C. Nur ein akzeptierter Wert erreicht die Zuweisung. Nach einem abgelehnten Versuch, 35 °C einzustellen, bleibt der Sollwert bei 22 °C. Auch eine numerische Zeichenkette wie "22" besteht die Prüfung mit Number.isFinite nicht.
Getter und Setter verwenden #target, sodass die Zuweisung an t.target den Setter nicht rekursiv aufruft. Die Namen target und #target sind verschieden. Ein vorangestellter Unterstrich wie in _target bittet andere Programmierer nur darum, eine öffentliche Eigenschaft als intern zu behandeln; JavaScript erlaubt weiterhin den Zugriff darauf.
Private Felder erscheinen nicht in Object.keys, for...in oder bei der JSON-Serialisierung. Dieses Thermostat hat keine eigenen öffentlichen Felder, daher ergibt JSON.stringify(t) den Wert "{}". Zum Speichern eines Objekts kann deshalb eine ausdrückliche öffentliche Methode nötig sein, die die zu speichernden Daten zurückgibt.
Interne Hilfsmethoden privat halten
Eine Methode wie #check(value) { ... } kann Validierungsschritte enthalten und wird innerhalb der Klasse als this.#check(value) aufgerufen. Sie ist nicht Teil der öffentlichen Schnittstelle. Innerhalb der Klasse prüft #target in obj, ob ein Objekt dieses private Feld besitzt.
Eine Unterklasse kann den privaten Namen ihrer Oberklasse nicht direkt verwenden. Sie kann den öffentlichen Getter oder Setter der Oberklasse verwenden, der in der Klasse ausgeführt wird, die das Feld deklariert hat. So behält die Oberklasse die Kontrolle über ihren eigenen Zustand.
Gemeinsam genutzte Aufgaben auf der Klasse ablegen
Ein statisches Mitglied gehört zur Klasse. Thermostat.MIN ist 10, während t.MIN den Wert undefined hat. Diese statischen Schranken sind öffentliche Felder; static allein macht sie nicht schreibgeschützt.
class Tracked {
static count = 0;
constructor() { Tracked.count++; }
}
new Tracked();
new Tracked();
new Tracked();
Tracked.count; // 3
Jeder Konstruktor erhöht einen Zähler auf Klassenebene. Ein Instanzfeld würde dagegen jedem Objekt einen eigenen Zähler geben. Eine statische Fabrikmethode wie static square(side) { return new Rectangle(side, side); } gehört in Rectangle und wird als Rectangle.square(5) aufgerufen. Eine statische Vergleichshilfsfunktion kann an sort übergeben werden, wenn sie die verglichenen Objekte als Argumente entgegennimmt.
Innerhalb einer statischen Methode ist this die Klasse, auf der sie aufgerufen wurde; es ist keine Rechteckinstanz mit einer Breite. Ein Initialisierungsblock static { ... } führt die Einrichtung einmal aus, wenn die Klasse definiert wird, und kann auf die privaten Mitglieder dieser Klasse zugreifen.
Wichtige Erkenntnisse
- Private
#-Felder werden von JavaScript geschützt; ein Unterstrich ist nur eine Konvention. - Validiere über die öffentliche Schnittstelle, bevor du privaten Zustand änderst.
- Private Felder fehlen bei der gewöhnlichen Aufzählung von Schlüsseln und bei der JSON-Serialisierung.
- Unterklassen nutzen die öffentliche Schnittstelle der Oberklasse, um mit deren privatem Zustand zu arbeiten.
- Rufe statische Mitglieder auf der Klasse auf; sie enthalten nicht die Daten der einzelnen Instanzen.
- Ein statischer Initialisierungsblock wird einmal ausgeführt, wenn seine Klasse definiert wird.
1.3 Spezialisiere eine Klasse durch Vererbung
Vererbung bildet eine „ist ein“-Beziehung ab. Ein Kreis ist eine Form, daher kann eine Klasse Circle die gemeinsame Beschreibung einer Form erben und zugleich eine eigene Flächenberechnung bereitstellen. Eine Unterklasse erbt Verhalten, ohne jede Methode in ihren Rumpf zu kopieren.
Initialisiere zuerst die Oberklasse
class Circle extends Shape macht Circle zu einer Unterklasse und Shape zu ihrer Oberklasse. In einem Unterklassenkonstruktor ruft super(...) den Konstruktor der Oberklasse auf. Dieser Aufruf muss vor der ersten Verwendung von this ausgeführt werden. Eine Unterklasse ohne Konstruktor erhält einen, der ihre Argumente an die Oberklasse weitergibt.
class Shape {
constructor(name) { this.name = name; }
area() { return 0; }
describe() {
return this.name + " with area " +
this.area().toFixed(2) + " cm²";
}
}
class Circle extends Shape {
constructor(radius) {
super("Circle");
this.radius = radius;
}
area() { return Math.PI * this.radius ** 2; }
}
class Rectangle extends Shape {
constructor(width, height) {
super("Rectangle");
this.width = width;
this.height = height;
}
area() { return this.width * this.height; }
}
Der Aufruf new Circle(2).describe() ergibt "Circle with area 12.57 cm²". Zuerst setzt super den Namen. Der Kreis speichert seinen Radius. Das geerbte describe() ruft dann this.area() auf, wodurch die Methode des Kreises gefunden wird. toFixed(2) formatiert das Ergebnis für die Anzeige. new Rectangle(3, 4).describe() ergibt "Rectangle with area 12.00 cm²".
Überschreibe oder erweitere eine Methode
Eine Unterklasse überschreibt eine Methode, indem sie eine Methode mit demselben Namen deklariert. Sie kann die Version der Oberklasse über super.methodName() aufrufen.
class Square extends Rectangle {
constructor(side) {
super(side, side);
this.name = "Square";
}
describe() {
return super.describe() +
" (all sides " + this.width + " cm)";
}
}
new Square(5).describe();
// "Square with area 25.00 cm² (all sides 5 cm)"
super(side, side) initialisiert Breite und Höhe mit demselben Wert. Die geerbte Flächenmethode multipliziert sie, und die überschreibende Methode ergänzt die geerbte Beschreibung um Informationen. Werden erforderliche Argumente für die Oberklasse vergessen, können ihre Eigenschaften den Wert undefined haben; der Aufruf von super() allein reicht nicht, wenn die Oberklasse Werte benötigt.
Polymorphie bedeutet, dass derselbe Aufruf unterschiedliches Verhalten auswählen kann. Eine Schleife kann s.describe() auf Kreisen und Rechtecken aufrufen; jede Beschreibung ruft das passende area() auf. Das Hinzufügen einer Form mit eigener Flächenmethode erfordert keinen neuen Fall in dieser Schleife.
Sowohl new Circle(2) instanceof Circle als auch new Circle(2) instanceof Shape sind wahr. Auch statische Mitglieder werden geerbt. Die Beziehung einer Instanz zu einer Klasse folgt ihrer Prototypenkette; genau diese prüft instanceof normalerweise.
Wähle eine sinnvolle Beziehung
Eine Klasse kann nur eine Oberklasse erweitern. Für eine „hat ein“-Beziehung verwende Komposition: Ein Auto hat einen Motor und fordert ihn mit this.engine.start() auf, Arbeit zu leisten. Die gemeinsame Nutzung einer Hilfsmethode allein ist kein Grund zu behaupten, dass eine nicht verwandte Klasse eine Art der anderen ist. Ein Mixin kopiert eine Gruppe von Methoden auf einen Prototyp, zum Beispiel mit Object.assign(Rectangle.prototype, serializable). Tiefe Ketten von Oberklassen können es erschweren, Änderungen nachzuvollziehen.
Auch eingebaute Klassen können erweitert werden. Ein Konstruktor von ValidationError extends Error ruft super(message) auf, setzt this.name = "ValidationError" und kann this.field = field speichern. Ein catch-Block kann mit err instanceof ValidationError diesen Fehler anders behandeln und die geerbte Meldung lesen.
Wichtige Erkenntnisse
- Verwende
extendsfür eine „ist ein“-Beziehung und Komposition für eine „hat ein“-Beziehung. - Rufe
super(...)mit den benötigten Argumenten für die Oberklasse auf, bevor duthisverwendest. - Eine überschreibende Methode kann das Verhalten der Oberklasse mit
super.methodName()wiederverwenden. - Polymorphie ermöglicht, dass ein Methodenaufruf bei verwandten Klassen funktioniert.
1.4 Verfolge die Eigenschaftssuche über Prototypen
Ein Objekt hat eine verborgene Verknüpfung zu einem anderen Objekt, seinem Prototyp, oder zu null. Beim Lesen einer fehlenden Eigenschaft wird dieser Verknüpfung gefolgt. JavaScript sucht weiter, bis es die Eigenschaft findet oder null erreicht; eine erfolglose Suche gibt undefined zurück.
Unterscheide gespeicherte und geerbte Werte
const defaults = { theme: "light", fontSize: 16 };
const settings = Object.create(defaults);
settings.fontSize = 18;
settings.theme; // "light"
settings.fontSize; // 18
defaults.fontSize; // 16
Object.hasOwn(settings, "theme"); // false
"theme" in settings; // true
Object.keys(settings); // ["fontSize"]
Object.create(defaults) erstellt ein leeres Objekt, dessen Prototyp defaults ist. Die Zuweisung an fontSize erstellt eine eigene Eigenschaft, die den geerbten Wert verdeckt. Sie lässt defaults.fontSize unverändert. Nach delete settings.fontSize erreicht die Suche wieder den geerbten Wert 16.
| Prüfung | Berücksichtigte Eigenschaften |
|---|---|
Object.hasOwn(obj, key) | Eigene Eigenschaften, ob aufzählbar oder nicht |
Object.keys(obj) | Eigene aufzählbare Zeichenkettenschlüssel |
key in obj | Eigene und geerbte Eigenschaften, ob aufzählbar oder nicht |
for...in | Aufzählbare Zeichenkettenschlüssel, einschließlich geerbter Schlüssel |
Eine fehlende eigene Eigenschaft kann dennoch in der Kette existieren. Klassenmethoden sind nicht aufzählbar, daher führt for...in sie normalerweise nicht auf. Lies den Prototyp des Objekts mit Object.getPrototypeOf(settings). Bevorzuge diese Object-Methoden gegenüber dem älteren Zugriff über __proto__.
Sieh, wie Klassen Methoden gemeinsam nutzen
Für eine Rechteckinstanz r ist Object.getPrototypeOf(r) === Rectangle.prototype wahr. Ihre Breite ist eine eigene Eigenschaft, während area normalerweise auf Rectangle.prototype gespeichert ist. Object.hasOwn(r, "area") ist falsch, obwohl r.area() funktioniert. Ein Pfeilfunktionsfeld verhält sich anders: Es erstellt auf jeder Instanz eine eigene Funktionseigenschaft.
Eine geerbte Methode erhält weiterhin das Objekt vor dem Punkt als this. Die gemeinsam genutzte Flächenmethode kann daher für jedes Rechteck andere Maße lesen, ohne auf jedem Objekt eine separate Methode zu speichern.
Für die Formenhierarchie folgt die Suche diesem Weg:
circle -> Circle.prototype -> Shape.prototype
-> Object.prototype -> null
Die Flächenmethode des Kreises wird zuerst gefunden. Seine Beschreibung stammt von Shape.prototype, und toString kann von Object.prototype stammen. Shape.prototype in der Kette erklärt auch, warum circle instanceof Shape wahr ist.
Der Name Rectangle.prototype bezeichnet den Prototyp, den Rechteckinstanzen erhalten. Er bezeichnet nicht den verborgenen Prototyp der Funktion Rectangle selbst; für diese Klasse ist das Shape, wenn sie Shape erweitert, oder Function.prototype bei einer Klasse ohne Oberklasse.
Erkenne älteren Konstruktorcode
function Rectangle(width, height) {
this.width = width;
this.height = height;
}
Rectangle.prototype.area = function () {
return this.width * this.height;
};
Konstruktorfunktionen verwenden dieselbe grundlegende Struktur von Instanz und Prototyp. Die Klassensyntax ergänzt Regeln: new ist erforderlich, der Rumpf läuft im strikten Modus, Methoden sind nicht aufzählbar und private Mitglieder sind verfügbar. typeof Rectangle ist bei beiden Formen "function".
Arrays finden map auf Array.prototype; Zeichenkettenmethoden stammen von String.prototype. Vermeide es, eingebaute Prototypen um eigene Methoden zu ergänzen: Bibliotheken und künftige Standards können dieselben Namen mit anderem Verhalten verwenden. Weil eine Bibliothek bereits flatten verwendete, erhielt die Standard-Arraymethode unter anderem deshalb den Namen flat.
Eine gewöhnliche Zuweisung an eine Dateneigenschaft erstellt oder ändert eine eigene Eigenschaft, aber ein geerbter Setter kann die Zuweisung abfangen. Den Prototyp eines vorhandenen Objekts mit Object.setPrototypeOf zu ändern, ist erlaubt, kann aber die Leistung beeinträchtigen; wähle den Prototyp beim Erstellen des Objekts.
Wichtige Erkenntnisse
- Beim Lesen einer Eigenschaft werden das Objekt, seine Prototypen und schließlich
nulldurchsucht. - Eine eigene Eigenschaft kann einen geerbten Wert verdecken, ohne den Prototyp zu ändern.
- Ob eine Eigenschaft dem Objekt selbst gehört und ob sie aufzählbar ist, sind verschiedene Fragen.
- Gewöhnliche Klassenmethoden liegen auf dem Prototyp der Instanz und stellen gemeinsam genutztes Verhalten bereit.
- Eine geerbte Methode verwendet die empfangende Instanz als
this. - Konstruktorfunktionen und Klassen bauen auf demselben Prototypenmechanismus auf.
- Lass eingebaute Prototypen unverändert, um Konflikte bei Namen und Verhalten zu vermeiden.
2Verbinde Dateien mit ES-Modulen
Module ermöglichen es, ein Programm über ein einzelnes Skript hinaus zu erweitern und dabei jede Datei verständlich zu halten. Exporte beschreiben, was eine Datei anbietet, und Importe zeigen, was sie benötigt. Lerne, wie diese Verbindung funktioniert, wie Browser und Node.js Module laden und wie du eine Funktion erst bei Bedarf lädst. Klare Abgrenzungen zwischen Daten, Speicherung und Seitenaktualisierungen machen Code zudem leichter testbar und helfen, zyklische Abhängigkeiten zu vermeiden.
2.1 Exportiere Funktionen und Werte und importiere ihre Bindungen
Ein ES-Modul hat einen eigenen Gültigkeitsbereich. Seine Deklarationen auf oberster Ebene bleiben innerhalb der Datei, sofern sie nicht exportiert werden. Importeure fordern bestimmte Funktionen und Werte an und machen so Abhängigkeiten sichtbar, ohne alles in globalen Variablen abzulegen.
Wähle die passende Exportform
// geometry.js
export const round2 = (n) => Math.round(n * 100) / 100;
export function circleArea(r) {
return Math.PI * r * r;
}
// shapes.js
import { circleArea, round2 } from "./geometry.js";
export default class Circle {
constructor(r) { this.r = r; }
area() { return round2(circleArea(this.r)); }
}
// main.js
import Circle from "./shapes.js";
console.log(new Circle(3).area()); // 28.27
Das Einstiegsmodul importiert eine Klasse. Das Modul der Klasse importiert zwei Hilfsfunktionen. Bei einem Radius von 3 cm beträgt die Fläche etwa 28,274 cm²; die Rundungshilfsfunktion ergibt 28,27. Das Einstiegsmodul erhält nicht automatisch Zugriff auf die Hilfsfunktionen, die innerhalb eines anderen Moduls verwendet werden.
| Export | Entsprechender Import |
|---|---|
export const MAX = 30; | import { MAX } from "./limits.js"; |
export default class Circle {} | import Circle from "./shapes.js"; |
Ein Standardexport und das benannte round2 | import Circle, { round2 } from "./shapes.js"; |
Ein Modul kann viele benannte Exporte, aber höchstens einen Standardexport haben. Benannte Importe verwenden den exportierten Namen in geschweiften Klammern; den lokalen Namen für einen Standardimport wählt der Importeur. import { Circle } fordert nicht den Standardexport an, auch wenn die exportierte Klasse Circle heißt.
Benenne um, gruppiere und reexportiere
import { circleArea as areaOfCircle } from "./geometry.js"; gibt einer importierten Funktion einen anderen lokalen Namen. export { circleArea as area }; ändert ihren exportierten Namen. Eine Exportliste kann auch Deklarationen verfügbar machen, die zuvor in der Datei erstellt wurden.
import * as geometry from "./geometry.js"; erstellt ein Modulnamensraumobjekt. Rufe geometry.circleArea(3) auf. Ein Namensraum enthält einen Standardexport unter der Eigenschaft default, sofern das Modul einen hat.
Eine Datei kann die Exporte anderer Dateien zusammenfassen: export { circleArea } from "./geometry.js"; leitet einen benannten Export weiter. export * from "./geometry.js"; leitet benannte Exporte weiter, aber nicht den Standardexport dieses Moduls.
Verstehe gemeinsame Auswertung und Live-Bindungen
Module laufen im strikten Modus und haben auf oberster Ebene undefined als this. Abhängigkeiten werden vor dem eigenen Rumpf des importierenden Moduls ausgewertet. Wird dasselbe aufgelöste Modul aus mehreren Dateien importiert, werden ein einmal ausgewertetes Modul und sein Zustand gemeinsam genutzt.
Angenommen, ein Modul exportiert let count = 0 und increment() ändert den Wert. Nachdem ein Importeur increment() aufgerufen hat, ergibt sein importiertes count beim Lesen 1. Importe sind Live-Bindungen, keine Momentaufnahmen. Der Importeur kann der Bindung selbst keinen neuen Wert zuweisen. Ist ein Export ein Objekt, kann dennoch eine seiner Eigenschaften geändert werden, und andere Importeure sehen die Änderung.
Statische Importdeklarationen gehören auf die oberste Ebene des Moduls. Ein statischer Import innerhalb eines if oder einer Funktion ist ein Syntaxfehler; dynamisches import() übernimmt das spätere Laden. Füge .js in relative ES-Modulpfade für Browser und Node.js ein: Diese Ladesysteme ergänzen die Endung nicht. Ein Pfadfehler oder eine Verwechslung von benanntem und Standardexport verhindert das Laden des Moduls. Korrigiere daher den Import, bevor du im Code, der danach laufen sollte, nach einem Fehler suchst.
Wichtige Erkenntnisse
- Module halten Deklarationen auf oberster Ebene lokal, sofern sie nicht exportiert werden.
- Benannte Importe verwenden geschweifte Klammern; ein Standardimport verwendet keine.
- Ein Namensraum gruppiert Exporte, während Reexporte die Schnittstelle eines Moduls weiterleiten.
- Importe sind schreibgeschützte Live-Bindungen an gemeinsam genutzten Modulzustand.
- Statische Importe gehören auf die oberste Ebene und relative ES-Modulpfade benötigen Dateiendungen.
2.2 Lade Module in Browsern und Node.js
Dieselbe ES-Modulsyntax läuft im Browser und in Node.js. Jede Umgebung muss dennoch wissen, dass eine Datei ein Modul ist, und jede hat ihre eigene Art, die angeforderten Pfade aufzulösen.
Starte ein Browsermodul
<script type="module" src="main.js"></script>
Lade nur das Einstiegsmodul mit einem script-Tag. Seine Importe rufen die anderen Dateien automatisch ab. Modulskripte ohne async werden verzögert ausgeführt: Sie laufen nach dem Parsen des HTML in Dokumentreihenfolge. Die Seitenelemente, auf die sie zugreifen, sind dann verfügbar.
Stelle den Ordner mit einem lokalen Server wie python3 -m http.server oder npx serve über HTTP bereit und öffne dessen Localhost-Adresse. Das Öffnen der HTML-Datei vom Datenträger führt wegen der Sicherheitsregeln des Browsers meist dazu, dass Modulanfragen fehlschlagen. Der Server muss JavaScript außerdem mit einem geeigneten MIME-Typ wie text/javascript zurückgeben.
Moduldeklarationen werden nicht global. Ein Inline-Handler wie onclick="save()" kann eine modullokale Funktion nicht finden. Registriere den Handler stattdessen aus JavaScript heraus:
const button = document.querySelector("#save");
function save() { console.log("Saved"); }
button.addEventListener("click", save);
Löse Pfade und Paketnamen auf
Der Modulbezeichner ist die Zeichenkette, die das Modul benennt. ./geometry.js ist relativ zur importierenden Datei; ../geometry.js führt einen Ordner nach oben. Ein mit / beginnender Pfad beginnt im Stammverzeichnis der Website, während eine vollständige HTTPS-URL den vollständigen Ort angibt.
Ein bloßer Name wie "date-fns" hat kein Pfadpräfix. Ein Browser kann dafür nicht node_modules durchsuchen. Eine vor den Modulskripten platzierte Import-Map ordnet einem bloßen Namen eine URL zu. Ein Entwicklungsserver oder Bundler kann stattdessen installierte Pakete auflösen und für den Browser vorbereiten. Die Installation eines Pakets allein ändert das Ladesystem des Browsers nicht.
Lege das Modulformat für Node.js ausdrücklich fest
| Datei oder Einstellung | Node.js-Format |
|---|---|
.mjs | ES-Modul |
.cjs | CommonJS |
.js unter "type": "module" | ES-Modul |
.js unter "type": "commonjs" | CommonJS |
Node.js liest type aus der nächstgelegenen übergeordneten package.json. Ohne ausdrücklichen Typ können aktuelle Versionen ES-Modulsyntax in einer ansonsten mehrdeutigen .js-Datei erkennen. Setze in einem neuen ES-Modulprojekt "type": "module", damit sich die Werkzeuge über das vorgesehene Format einig sind. Die Endungen .mjs und .cjs haben Vorrang vor dieser Einstellung.
Mit den drei Dateien des Kreisbeispiels und { "type": "module" } gibt node main.js den Wert 28.27 aus. Dieselben Dateien funktionieren über ein Browsermodulskript, das über HTTP bereitgestellt wird.
Erkenne CommonJS und eingebaute Module
CommonJS ist das ältere Modulsystem von Node.js. Es verwendet require() zum synchronen Laden und module.exports zum Bereitstellen von Werten. Ein ES-Modul kann module.exports eines CommonJS-Pakets als Standardexport importieren. Aktuelles Node.js kann auch synchrone ES-Modulgraphen mit require laden; Top-Level-await in diesem Graphen verhindert das synchrone Laden.
Verwende in einem ES-Modul import { readFile } from "node:fs/promises"; für ein eingebautes Node.js-Modul. require, module und __dirname sind CommonJS-Variablen, keine globalen ES-Modulvariablen. import.meta.url liefert die URL des Moduls; neuere Node.js-Versionen stellen für lokale Dateimodule auch import.meta.dirname bereit.
Wird eine Importanweisung abgelehnt, weil sie außerhalb eines Moduls steht, prüfe die Einstellung des Ladesystems: type="module" im Browser-HTML oder den Pakettyp beziehungsweise die Dateiendung in Node.js.
Wichtige Erkenntnisse
- Ein Browser-Einstiegsmodul verwendet
type="module"und wird normalerweise verzögert ausgeführt. - Verwende einen lokalen Server und registriere Event-Handler aus dem Modul heraus.
- Browser benötigen eine Import-Map oder Buildwerkzeuge, um bloße Paketnamen aufzulösen.
- Node.js verwendet Dateiendungen und das Feld
typedes nächstgelegenen Pakets. - CommonJS verwendet
requireundmodule.exports; ES-Module verwendenimportundexport.
2.3 Lade Funktionen bei Bedarf und trenne Zuständigkeiten
Ein statischer Import deklariert eine Abhängigkeit, die beim Laden eines Moduls benötigt wird. Ein dynamischer Import ist ein Ausdruck: Er beginnt mit dem Laden, wenn die Ausführung ihn erreicht, und gibt ein Promise zurück. Verwende ihn für eine Funktion, die bei vielen Besuchen gar nicht benötigt wird.
Warte auf das Namensraumobjekt
const button = document.querySelector("#show-chart");
button.addEventListener("click", async () => {
try {
const { drawChart } = await import("./chart.js");
drawChart(measurements);
} catch (err) {
console.error("Could not load the chart module", err);
}
});
Hier sind measurements die Daten, die die Seite gesammelt hat. Der erste Klick lädt das Diagrammmodul und ruft seine exportierte Funktion auf. Spätere Klicks verwenden das ausgewertete Modul erneut. Das Promise kann bei einem Lade- oder Auswertungsfehler abgelehnt werden, daher fängt der Handler den Fehler ab.
import("./chart.js").drawChart versucht, eine Eigenschaft des Promise zu lesen, nicht des geladenen Moduls. Warte auf das Promise oder verwende .then. Greife bei einem Standardexport auf die Eigenschaft default des Namensraums zu: const { default: Chart } = await import("./chart.js");. Anders als eine statische Deklaration kann dieser Ausdruck innerhalb einer Funktion oder Bedingung stehen und sein Pfad kann zur Laufzeit berechnet werden.
Warte während der Moduleinrichtung
Ein ES-Modul kann Top-Level-await außerhalb einer Funktion verwenden:
const response = await fetch("./config.json");
export const config = await response.json();
Module, die von diesem Modul abhängen, warten, bis seine Auswertung abgeschlossen ist, bevor ihre eigenen Rümpfe ausgeführt werden. Unabhängige Module können fortfahren. Eine langsame Konfigurationsanfrage verzögert daher die davon abhängigen Module, aber nicht unbedingt jeden Teil des Programms. Klassische Skripte und CommonJS erlauben kein Top-Level-await; verwende diese Modulfunktion für Vorbereitungen, die abhängige Module tatsächlich zuerst benötigen.
Gib jedem Modul eine klare Aufgabe
Eine Aufgabenanwendung kann diese Zuständigkeiten verwenden:
| Modul | Zuständigkeit |
|---|---|
todo.js | Die Klasse Todo exportieren |
storage.js | Daten über localStorage laden und speichern |
ui.js | Die Liste auf der Seite rendern |
main.js | Module und Event-Handler verbinden |
Die Daten- und Speichermodule importieren die Benutzeroberfläche nicht. Sie können getestet und wiederverwendet werden, ohne Seitenelemente zu erstellen. Wenn Berechnung und Speicherung außerhalb von Event-Handlern bleiben, lassen sich Fehler auch leichter eingrenzen.
Privater Zustand auf oberster Ebene wird von allen Importeuren gemeinsam genutzt. In let nextId = 1; export function createId() { return nextId++; } bleibt die Variable privat, während die Funktion kontrollierten Zugriff ermöglicht. Zwei Importeure, die sie jeweils einmal aufrufen, erhalten 1 und 2, nicht zwei separate Zähler, die bei 1 beginnen.
Steuere die Arbeit beim Import
import "./setup.js"; ist ein Seiteneffektimport: Er lädt das Modul und wertet es aus, ohne lokale Namen anzufordern. Seiteneffekte können für die Einrichtung nützlich sein, aber ein wiederverwendbares Modul lässt sich leichter testen, wenn es auf oberster Ebene Funktionen und Werte definiert und seine Funktionen die Arbeit ausdrücklich starten.
Eine Index- oder Barrel-Datei sammelt Reexporte. Sie kann eine Schnittstelle vereinfachen, aber große Barrel-Dateien können zusätzlichen Code einbeziehen und einen Zyklus verbergen. Eine zyklische Abhängigkeit entsteht, wenn Module direkt oder über andere Module voneinander abhängen. Ein Zyklus kann funktionieren, aber das Lesen einer Bindung, bevor das exportierende Modul sie initialisiert, verursacht einen ReferenceError. Verschiebe gemeinsam genutzte Definitionen in ein drittes Modul, das beide Seiten importieren, um diesen Abhängigkeitszyklus aufzulösen.
Wichtige Erkenntnisse
import()gibt ein Promise für einen Modulnamensraum zurück und kann innerhalb einer Funktion ausgeführt werden.- Warte auf das dynamische Laden und behandle abgelehnte Promises.
- Top-Level-await verzögert die Auswertung abhängiger Module.
- Trenne Daten, Speicherung, Rendern und die Verknüpfung von Ereignissen in Module mit klaren Aufgaben.
- Ein ausgewertetes Modul kann privaten Zustand enthalten, den alle seine Importeure gemeinsam nutzen.
- Verschiebe gemeinsam genutzte Definitionen in ein drittes Modul, um eine zyklische Abhängigkeit aufzulösen.
3Führe ein reproduzierbares Projekt mit npm aus
Ein Projekt benötigt eine klare Beschreibung seiner Befehle und der Pakete, von denen es abhängt. npm hält diese Informationen in Paketdateien fest und installiert den Code, den diese Dateien beschreiben. Lerne, Skripte vor ihrer Ausführung zu lesen, Entwicklungswerkzeuge von Produktabhängigkeiten zu unterscheiden und Versionsbereiche zusammen mit einer Lockdatei zu verwenden. So kann ein anderer Computer das Projekt wiederherstellen, ohne einen installierten Paketordner zu kopieren.
3.1 Beschreibe ein npm-Projekt und führe es aus
npm ist der mit Node.js gelieferte Paketmanager. Der Name bezeichnet sowohl ein Kommandozeilenwerkzeug als auch dessen öffentliche Paket-Registry. Ein Paket ist ein durch package.json beschriebener Ordner; der Stammordner eines Projekts ist der Ordner, der diese Datei enthält.
Erstelle die Projektbeschreibung
Führe node -v und npm -v aus, um die installierten Werkzeuge zu prüfen. Wechsle in den vorgesehenen Projektordner, bevor du npm init ausführst. Der interaktive Befehl stellt Fragen; npm init -y übernimmt die Standardwerte. Bearbeite dann die Datei, um das Projekt zu beschreiben.
{
"name": "shape-lab",
"version": "1.0.0",
"private": true,
"type": "module",
"scripts": {
"start": "node src/main.js",
"test": "node --test",
"lint": "eslint ."
},
"dependencies": {},
"devDependencies": {}
}
Dies ist JSON, kein JavaScript-Objektliteral. Schlüssel und Zeichenketten stehen in doppelten Anführungszeichen. Kommentare und nachgestellte Kommas sind ungültig. Ein Parserfehler kann npm stoppen, bevor es einen Befehl ausführt.
Der name wird kleingeschrieben und ist URL-tauglich; veröffentlichte Paketnamen haben höchstens 214 Zeichen. version beschreibt dieses Projekt, nicht seine Node.js-Version. private: true verhindert eine versehentliche Veröffentlichung über npm. type: "module" sorgt dafür, dass Node.js die .js-Dateien des Pakets als ES-Module lädt.
Veröffentlichte Pakete können mit main und exports Einstiegspunkte für ihre Nutzer definieren. Ein optionaler Eintrag engines wie { "node": ">=22" } gibt unterstützte Node.js-Versionen an. Lies diese Anforderungen, wenn ein Projekt auf einer älteren Installation fehlschlägt.
Nutze Skripte als Aufgabenmenü
npm run listet verfügbare Skripte auf. In diesem Beispiel führt npm run lint den Befehl eslint . aus, npm start startet die Anwendung und npm test führt den Testrunner aus. start und test haben diese Kurzformen; andere Aufgabennamen verwenden normalerweise npm run.
npm fügt das Verzeichnis node_modules/.bin des Projekts vorübergehend zum Suchpfad für Befehle hinzu. Ein lokal installiertes Werkzeug kann daher direkt in einem Skript genannt werden. Alle, die das Projekt nutzen, führen dasselbe festgehaltene Werkzeug aus, statt sich auf eine globale Installation zu verlassen.
Zusätzliche Argumente folgen auf --: npm run lint -- --fix übergibt --fix an ESLint. Das Skript gibt der Aufgabe einen stabilen Namen, auch wenn das Team das dahinterstehende Werkzeug ändert. Beginne mit dem Feld scripts, wenn du lernst, wie du an einem unbekannten Projekt arbeitest.
Lies Versionsnummern
Die semantische Versionierung verwendet HAUPTVERSION.NEBENVERSION.PATCH. Ausgehend von 2.4.1 wird eine kompatible Fehlerkorrektur zu 2.4.2, eine kompatible Funktion zu 2.5.0 und eine inkompatible Änderung zu 3.0.0. Ein Wechsel auf eine neue Hauptversion kann Änderungen an Code erfordern, der das Paket verwendet. Vor 1.0.0 befindet sich ein Projekt in früher Entwicklung und Veröffentlichungen können sich freier ändern.
Führe gelegentlich einen Paketbefehl aus
npx serve führt den installierten Befehl aus, wenn er lokal verfügbar ist. Andernfalls kann npx nachfragen, ob es das Paket vorübergehend herunterladen und ausführen soll. Das ist für ein Werkzeug nützlich, das keine dauerhafte Projektabhängigkeit werden muss. Prüfe den genauen Paketnamen und die Vertrauenswürdigkeit des Codes, bevor du ihn installierst oder ausführst: Paketbefehle und Installationsskripte können mit deinen Berechtigungen ausgeführt werden.
Ein im falschen Ordner ausgeführter Befehl kann fehlschlagen, weil package.json fehlt, oder eine Installation kann Paketdateien an einem unbeabsichtigten Ort erstellen. Prüfe zuerst den aktuellen Ordner. Halte Skripte verständlich und vermeide betriebssystemspezifische Befehlskniffe, wenn das Projekt auf mehreren Systemen laufen muss.
Wichtige Erkenntnisse
package.jsonbeschreibt Projekt, Skripte und Abhängigkeiten mit gültigem JSON.private: trueverhindert die Veröffentlichung über npm;typelegt das Node.js-Modulformat fest.- Nutze npm-Skripte, um lokal installierte Werkzeuge einheitlich auszuführen.
- Semantische Versionen unterscheiden inkompatible Änderungen, kompatible Funktionen und Fehlerkorrekturen.
npxkann einen lokalen Befehl ausführen oder einen vorübergehend herunterladen.
3.2 Installiere und aktualisiere die richtigen Paketversionen
Eine Abhängigkeit ist Code, auf den ein Projekt angewiesen ist. npm hält direkte Abhängigkeiten in package.json fest, installiert Pakete in node_modules und verzeichnet den genauen installierten Abhängigkeitsbaum in package-lock.json.
Trenne Produktcode von Entwicklungswerkzeugen
npm install date-fns fügt ein Laufzeitpaket zu dependencies hinzu. npm install --save-dev eslint fügt ein Entwicklungswerkzeug zu devDependencies hinzu; -D ist die Kurzform. Linter, Codeformatierer, Testrunner, Bundler und TypeScript gehören normalerweise in die Entwicklungsliste.
Wenn ein anderes Projekt ein veröffentlichtes Paket installiert, installiert npm dessen Abhängigkeiten, aber nicht dessen Entwicklungsabhängigkeiten. Eine Browseranwendung kann Laufzeitabhängigkeiten dennoch in ihre endgültigen Dateien bündeln, obwohl das Buildwerkzeug selbst eine devDependency ist.
npm install ohne Paketnamen installiert die für das Projekt aufgeführten Abhängigkeiten. npm uninstall <package> entfernt ein Paket und seinen Eintrag. Nach der Installation von date-fns können Node.js und Buildwerkzeuge import { format } from "date-fns"; auflösen. Ein einfacher Browser benötigt weiterhin eine Auflösungshilfe wie eine Import-Map oder einen Entwicklungsserver.
Lies den erlaubten Bereich
| Eintrag | Erlaubte stabile Versionen |
|---|---|
^4.17.21 | Mindestens 4.17.21, unter 5.0.0 |
~4.17.21 | Mindestens 4.17.21, unter 4.18.0 |
4.17.21 | Genau 4.17.21 |
^0.3.1 | Mindestens 0.3.1, unter 0.4.0 |
Das Caret ist der übliche Standard von npm. Bei Versionen ab 1.0.0 erlaubt es kompatible Aktualisierungen der Neben- und Patchversion. Unter 1.0.0 ist es strenger. Für ^2.3.0 passen 2.3.5 und 2.9.0; 2.2.9 ist zu alt und 3.0.0 überschreitet die Grenze zur nächsten Hauptversion.
Ein erlaubter Bereich bezeichnet keine einzelne installierte Version. Das tut die Lockdatei, einschließlich der transitiven Abhängigkeiten, also der Pakete, die deine ausgewählten Pakete benötigen.
Stelle eine Installation wieder her
Gib package.json und package-lock.json zusammen mit dem Projekt weiter. Lass npm die Lockdatei verwalten. Der generierte Ordner node_modules kann neu erstellt werden und wird nicht weitergegeben.
Verwende npm install während der Entwicklung, wenn sich die Abhängigkeitseinträge ändern können. Verwende npm ci für eine saubere, reproduzierbare Installation: Es benötigt die Lockdatei, entfernt ein vorhandenes node_modules, installiert den festgelegten Baum und schreibt keine der beiden Paketdateien neu. Es schlägt fehl, wenn Paketdatei und Lockdatei nicht übereinstimmen. Behebe diese Abweichung während der Entwicklung bewusst; das Löschen der Lockdatei kann viele Versionen unbemerkt ändern.
Aktualisiere aus einem bestimmten Grund
npm outdated vergleicht die aktuelle, die gewünschte und die neueste Version. Die gewünschte Version ist die neueste, die der deklarierte Bereich erlaubt; die neueste kann eine Hauptversion außerhalb dieses Bereichs sein. npm update aktualisiert innerhalb der Bereiche und hält den resultierenden Baum fest. Um bewusst zu einer neuen Hauptversion zu wechseln, lies das Änderungsprotokoll, führe npm install <package>@latest aus und prüfe das Projekt mit seinen Tests.
Installiere Projektwerkzeuge lokal, damit ihre Versionen festgehalten werden. Prüfe vor dem Hinzufügen einer weiteren Abhängigkeit deren Pflege, Verbreitung und Lizenz sowie die Frage, ob eine eingebaute Funktion das Problem bereits löst. structuredClone() deckt unterstützte Anwendungsfälle für tiefe Kopien ab; Intl.DateTimeFormat formatiert Datumsangaben ohne Datumspaket.
npm audit meldet bekannte Sicherheitslücken in installierten Versionen. Lies den Bericht, um den betroffenen Code und seine Nutzung zu verstehen. npm audit fix kann kompatible Korrekturen anwenden, beweist aber weder, dass jede Sicherheitslücke behoben ist, noch, dass eine Aktualisierung das Verhalten des Projekts erhält.
Wichtige Erkenntnisse
- Verwende
dependenciesfür Laufzeitcode unddevDependenciesfür Entwicklungs- und Buildwerkzeuge. - Versionsbereiche erlauben bestimmte Versionen; die Lockdatei hält die genauen installierten Versionen fest.
- Gib beide Paketdateien weiter und erstelle
node_modulesneu. npm ciinstalliert die in der Lockdatei festgelegten Pakete, ohne die Datei zu ändern, und lehnt nicht übereinstimmende Paketdateien ab.npm updatebleibt innerhalb deklarierter Bereiche; für den Wechsel zu einer neuen Hauptversion ist eine bewusste Entscheidung nötig.- Lies Sicherheitsberichte und prüfe das Verhalten nach Aktualisierungen von Abhängigkeiten.
4Prüfe und baue Code mit Entwicklerwerkzeugen
Verschiedene Werkzeuge beantworten verschiedene Fragen zu einem Projekt. Ein Linter sucht nach verdächtigem Code, ein Codeformatierer ordnet ihn, Tests prüfen sein Verhalten und ein Build bereitet Browserdateien für die Veröffentlichung vor. Ein Entwicklungsserver hält den Bearbeitungsablauf schnell. Lerne, was jedes Ergebnis aussagt und was es ungeprüft lässt, damit ein erfolgreicher Formatierungsbefehl oder eine funktionierende Entwicklungsseite nicht als Beleg dafür gilt, dass die ganze Anwendung funktioniert.
4.1 Finde Codeprobleme und formatiere einheitlich
Ein Linter liest Quellcode, ohne ihn auszuführen, und meldet wahrscheinliche Fehler oder riskante Muster. Ein Codeformatierer gestaltet das Layout einheitlich um. ESLint und Prettier arbeiten oft zusammen, weil diese Aufgaben unterschiedliche Probleme lösen.
Konfiguriere die Prüfungen
npm init @eslint/config@latest stellt Fragen zum Projekt, installiert Entwicklungspakete und erstellt eine Konfigurationsdatei. Aktuelles ESLint verwendet eine flache Konfiguration, üblicherweise in eslint.config.js.
import js from "@eslint/js";
import globals from "globals";
export default [
js.configs.recommended,
{
languageOptions: { globals: globals.browser },
rules: {
eqeqeq: "error",
"no-unused-vars": "warn",
},
},
];
Die empfohlene Konfiguration aktiviert Regeln zur Fehlersuche. Die globalen Browsernamen teilen ESLint mit, dass Namen wie document existieren. So wird eine tatsächlich vorhandene Funktion der Umgebung nicht als undefinierter Name behandelt.
Die Schweregrade der Regeln sind "off", "warn" und "error", auch als 0, 1 und 2 geschrieben. Fehler führen zu einem Fehlercode; Warnungen allein lassen den Befehl standardmäßig nicht fehlschlagen. Auch wenn das Skript erfolgreich endet, enthält eine Warnung Informationen, die du lesen solltest.
Lies einen Linter-Bericht
function total(items) {
let sum = 0;
let unused = 5;
for (const item of items) {
if (item.quantity == "2") sum += item.price;
}
return summ;
}
Mit der obigen Konfiguration werden fünf Probleme gemeldet. total wird nie verwendet. sum wird aktualisiert, aber sein Wert wird über diese Aktualisierungen hinaus nie verwendet. unused wird nicht verwendet. Die Regel eqeqeq beanstandet ==, und no-undef erkennt das falsch geschriebene summ. Ein Aufruf dieser Funktion würde an diesem undefinierten Namen fehlschlagen, aber der Linter kann ihn ohne Aufruf finden.
Beginne im Bericht mit dem Namen der Regel und der Fundstelle. Korrigiere das beabsichtigte Verhalten und führe den Befehl erneut aus. npm run lint -- --fix fordert sichere automatische Korrekturen an, wenn das Projekt ein Skript lint hat. Der Befehl kann nicht für jede Warnung die richtige Programmlogik bestimmen. Deaktiviere eine Regel für eine Zeile nur, wenn es einen Grund gibt, und halte diesen neben dem Kommentar fest.
Überlasse das Layout einem Codeformatierer
npm install --save-dev --save-exact prettier
npx prettier . --write
npx prettier . --check
--write ändert Dateien; --check meldet Formatierungsunterschiede, ohne Dateien zu ändern. Mit den Standardeinstellungen wird aus const point={x:1,y:2} der Code const point = { x: 1, y: 2 };. Optionen wie "singleQuote": true stehen in .prettierrc.
| Werkzeug | Hauptfrage |
|---|---|
| ESLint | Enthält dieser Code ein Muster, das eine Regel beanstandet? |
| Prettier | Hat der Code das gewählte Layout? |
| Testrunner | Zeigt der Code das erwartete Verhalten? |
Die empfohlenen ESLint-Regeln schreiben das Layout von Prettier nicht vor, sodass diese Einrichtung Formatierungskonflikte vermeidet. Fügt eine andere ESLint-Konfiguration widersprüchliche Stilregeln hinzu, schaltet eslint-config-prettier diese Regeln aus. Werden zwei konkurrierende Codeformatierer ausgeführt, können sich Dateien ständig hin und her ändern.
Füge Skripte lint und format hinzu, damit sich Prüfungen leicht wiederholen lassen. Editor-Erweiterungen können nach der Konfiguration während der Eingabe Diagnosen anzeigen und beim Speichern formatieren. Beide Werkzeuge überspringen node_modules standardmäßig; füge generierte Ausgaben wie dist zu den passenden Ignoriereinstellungen hinzu. Eine Funktion ohne Linter-Meldungen kann trotzdem den Umfang berechnen, obwohl sie die Fläche berechnen sollte; das Verhalten braucht Tests.
Wichtige Erkenntnisse
- Ein Linter findet konfigurierte Quellcodeprobleme, ohne das Programm auszuführen.
- Der ESLint-Schweregrad für Fehler lässt den Befehl fehlschlagen; Warnungen tun das standardmäßig nicht.
- Prettier ändert das Layout mit
--writeund prüft es mit--check. - Verwende kompatible Linter- und Formatierungsregeln sowie Tests für das Verhalten.
4.2 Entwickle lokal und baue Browserdateien
Ein Browser kann einige ES-Module direkt laden, aber größere Projekte verwenden auch installierte Pakete und Dateien, die transformiert werden müssen. Ein Entwicklungsserver bereitet Dateien während der Bearbeitung vor. Ein Bundler folgt Abhängigkeiten, um für die Veröffentlichung geeignete Dateien zu erstellen.
Durchlaufe den Vite-Zyklus
npm create vite@latest shape-lab
cd shape-lab
npm install
npm run dev
npm run build
npm run preview
Wähle für dieses Projekt die JavaScript-Vorlage Vanilla. Der Generator stellt Entwicklungs- und Buildskripte bereit. npm run dev gibt eine lokale Adresse aus, häufig http://localhost:5173. Öffne diese Adresse während der Bearbeitung. In Vite ist index.html der Einstiegspunkt und lädt /src/main.js normalerweise mit einem Modulskript.
Der Entwicklungsserver löst Paketnamen auf und transformiert Dateien nach Bedarf. Das Speichern einer Datei löst eine Aktualisierung aus. Hot Module Replacement, kurz HMR, ersetzt ein geändertes Modul auf der laufenden Seite; bei unterstützten Aktualisierungen kann der Zustand erhalten bleiben, statt ein vollständiges Neuladen zu erzwingen.
npm run build schreibt optimierte Ausgaben nach dist. npm run preview stellt diese Ausgabe lokal bereit, damit du den tatsächlichen Produktionsbuild prüfen kannst. Die Vorschau benötigt einen vorhandenen Build und erstellt keinen neuen. Bearbeite die Quelldateien, da ein weiterer Build die generierte Ausgabe ersetzt.
Verstehe die Änderungen im Produktionsbuild
| Buildfunktion | Zweck |
|---|---|
| Bündelung | Module zu weniger Browserdateien zusammenfassen |
| Tree Shaking | Code entfernen, den der Build nachweislich nicht benötigt |
| Minifizierung | Größe durch Entfernen des Layouts und Verkürzen von Namen verringern |
| Inhaltshashes | Dateinamen bei Inhaltsänderungen ändern und so das Caching unterstützen |
| Code-Splitting | Teile des Codes in separaten Abschnitten halten, die später geladen werden können |
Ein Hilfsmodul kann zum Beispiel sowohl formatArea als auch formatVolume exportieren. Wenn nur formatArea verwendet wird und das Entfernen des anderen Codes sicher ist, kann Tree Shaking formatVolume weglassen. Statische Importe und Exporte machen Abhängigkeiten sichtbar. Seiteneffekte eines Moduls auf oberster Ebene müssen möglicherweise erhalten bleiben, auch wenn keiner seiner Exporte verwendet wird.
Ein dynamisches import() gibt dem Buildwerkzeug eine Grenze für das spätere Laden. Buildwerkzeuge können dafür separate Abschnitte erstellen; deren Aufteilung hängt vom Build ab, sodass ein Quellmodul nicht immer zu genau einer Ausgabedatei wird.
Ein Name wie assets/index-4f2a9c1b.js ändert sich mit seinem Inhalt und ermöglicht so langlebige Browser-Caches, ohne nach einer Aktualisierung eine alte Datei auszuliefern. Eine Source Map verbindet Stellen in der generierten Ausgabe mit ursprünglichen Quellcodezeilen und erleichtert damit Stacktraces und das Debuggen, wenn solche Zuordnungen verfügbar sind.
Binde Ressourcen und öffentliche Konfiguration ein
Vite verarbeitet auch Ressourcen: import "./style.css"; bindet Stile ein und import logoUrl from "./logo.png"; liefert die endgültige URL des Bildes. Der Build kann diese Verweise aktualisieren, damit sie zu den generierten Dateinamen passen.
Lies Modusinformationen über import.meta.env.MODE. Die Standardwerte sind "development" für die Entwicklung und "production" für einen Build; ein benutzerdefinierter Modus kann einen anderen Namen verwenden. Benutzerdefinierte Umgebungswerte, die mit VITE_ beginnen, werden dem Browsercode standardmäßig zur Verfügung gestellt. Alles, was an den Browser ausgeliefert wird, ist öffentlich: Bewahre geheime Schlüssel und Passwörter auf einem Server auf, außerhalb des Browsercodes und offengelegter Umgebungsvariablen.
Prüfe nicht nur die Entwicklungsseite. Erstelle vor der Veröffentlichung einen Build und sieh ihn dir in der Vorschau an, um Probleme in der transformierten Ausgabe und bei Ressourcenpfaden zu finden. dist/index.html direkt vom Datenträger zu öffnen, entspricht nicht der Bereitstellung der Website über HTTP. Eine kleine Seite mit wenigen einfachen Modulen benötigt möglicherweise nur einen statischen Server und kein vollständiges Buildwerkzeug.
Wichtige Erkenntnisse
- Ein Entwicklungsserver löst Dateien während der Bearbeitung auf und transformiert sie; ein Produktionsbuild erstellt die Ausgabe für die Veröffentlichung.
- HMR kann geänderten Code ersetzen und dabei unterstützten Seitenzustand erhalten.
- Tree Shaking entfernt Code nur, wenn der Build ihn sicher weglassen kann.
- Inhaltshashes unterstützen das Caching und Source Maps verbinden die Ausgabe mit dem Quellcode.
- Erstelle zuerst den Build und sieh dir dann die generierte Ausgabe lokal in der Vorschau an.
- Browser-Bundles und offengelegte Umgebungswerte sind öffentlich.
4.3 Teste Verhalten mit Assertions
Ein automatisierter Test führt Code mit bekannten Eingaben aus und prüft das Ergebnis mit Assertions. Ein Unittest konzentriert sich auf einen kleinen Teil, etwa eine Funktion oder Methode. Der Testrunner findet Tests, führt sie aus, meldet Fehler und endet mit einem Fehlercode, wenn ein Test fehlschlägt.
Vorbereiten, ausführen, prüfen
Bereite die Eingabe vor, führe den Code aus und prüfe das erwartete Ergebnis. Ein hilfreicher Testname beschreibt das Verhalten. Für eine Rechteckklasse, deren Konstruktor eine negative Seitenlänge ablehnt:
import { test } from "node:test";
import assert from "node:assert/strict";
import { Rectangle } from "./rectangle.js";
test("area multiplies width by height", () => {
assert.equal(new Rectangle(3, 4).area(), 12);
});
test("rejects a negative width", () => {
assert.throws(() => new Rectangle(-1, 4), RangeError);
});
Die erste Assertion prüft 12 cm², ein unabhängig bekanntes Ergebnis. Die zweite übergibt eine Funktion an assert.throws, damit die Assertion den Fehler erfassen kann. Bei der Ausführung von node --test werden Dateien mit Namen wie rectangle.test.js gefunden; ein Paketskript "test": "node --test" ermöglicht npm test.
Der strikte Assertion-Modus verwendet für assert.equal die Methode Object.is ohne implizite Typumwandlung. assert.deepEqual vergleicht den Inhalt von Objekten und Arrays. Verschiedene Arrays mit denselben Werten sind nicht dasselbe Objekt, daher ist ein strikter Identitätsvergleich die falsche Prüfung auf gleichen Inhalt.
Nutze entsprechende Konzepte in Vitest
import { describe, it, expect } from "vitest";
import { Rectangle } from "./rectangle.js";
describe("Rectangle", () => {
it("computes the area", () => {
expect(new Rectangle(3, 4).area()).toBe(12);
});
it("rejects a negative width", () => {
expect(() => new Rectangle(-1, 4)).toThrow(RangeError);
});
});
Vitest verwendet describe zum Gruppieren und it für einzelne Tests. Führe npx vitest aus, um Änderungen zu überwachen, oder npx vitest run für einen einmaligen Durchlauf. Jest bietet einen ähnlichen Stil. Verwende toEqual für Inhalte und toBe für Identität oder primitive Werte.
Beziehe Grenzwerte und numerische Schranken ein
Teste die genauen Endpunkte eines erlaubten Bereichs ebenso wie Eingaben knapp außerhalb. Ein Thermostat, das 10 bis 30 °C akzeptiert, benötigt Tests, die 10 und 30 akzeptieren, sowie Tests, die 9,5 und 30,5 ablehnen. Ergänze separate Prüfungen für nicht numerische und nicht endliche Werte, wenn die Schnittstelle sie ablehnt. Tests sollten nicht von einem durch einen vorherigen Test veränderten Objekt oder von der Ausführungsreihenfolge abhängen.
Gleitkommaarithmetik stimmt möglicherweise nicht genau mit einem geschriebenen Dezimalwert überein: 0.1 + 0.2 ist 0.30000000000000004. Verwende für eine passende Toleranz assert.ok(Math.abs(actual - expected) < 1e-9). Ein Kreis mit Radius 3 hat eine Fläche von etwa 28,274333882 cm²; toBeCloseTo(28.27, 2) von Vitest prüft die Nähe auf diese Dezimalgenauigkeit. Wähle die Toleranz passend zu dem, was der Test nachweisen soll.
Lerne aus einem Fehlschlag
Lies das benannte Verhalten, den erwarteten und den tatsächlichen Wert sowie die Stelle im Quellcode. Entscheide, ob die Implementierung oder die Erwartung des Tests falsch ist. Schreibe bei der testgetriebenen Entwicklung einen fehlschlagenden Test, implementiere genug, damit er besteht, und verbessere dann den Code, während die Tests weiterhin bestehen.
Exportiere Logik aus Modulen und übergib Eingaben als Argumente. Ein Event-Handler kann dann Seitendaten sammeln und diese Logik aufrufen, statt Berechnungen mit DOM-Arbeit zu vermischen. DOM-abhängiger Code kann einen simulierten Browser wie jsdom verwenden. Warte in einem asynchronen Test auf asynchrone Arbeit, damit der Test nicht vor seinen Prüfungen endet. Ein Test, der lediglich die Formel der Implementierung wiederholt, kann deren Fehler wiederholen; verwende das erwartete Verhalten und unabhängige Beispiele.
Wichtige Erkenntnisse
- Tests führen Code aus und prüfen mit Assertions unabhängig erwartetes Verhalten.
- Bereite Eingaben vor, führe den Code aus und prüfe ein Verhalten mit einem hilfreichen Testnamen.
- Vergleiche den Inhalt verschiedener Arrays oder Objekte, wenn ihre Inhalte gleich sein sollen.
- Teste Grenzwerte und ungültige Eingaben und verwende geeignete Toleranzen für Gleitkommazahlen.
- Warte auf asynchrone Arbeit und halte Tests unabhängig von der Ausführungsreihenfolge.
5Ordne Typen und Frameworks in den Arbeitsablauf ein
TypeScript und Frontend-Frameworks bauen auf den bereits vorgestellten JavaScript-Werkzeugen auf. Typen helfen einem Editor und der Typprüfung, nicht passende Werte vor der Ausführung zu finden. Komponenten helfen, Oberflächen zu gliedern, die Daten gemeinsam nutzen und ändern. Beides ersetzt nicht das Verständnis von Modulen, Abhängigkeiten oder Tests. Nutze die Projektskripte als Wegweiser für das Zusammenspiel dieser Teile und füge Werkzeuge hinzu, wenn ihre Aufgabe klar ist.
5.1 Verstehe, was TypeScript prüft
TypeScript ergänzt JavaScript um statische Typsyntax. Ein Typ beschreibt, welche Art von Wert der Code erwartet. Die Typprüfung nutzt diese Beschreibungen, bevor das Programm läuft, und kann so Fehler aufdecken, bevor Nutzer darauf stoßen.
Prüfe einen Aufruf vor der Ausführung
function area(width: number, height: number): number {
return width * height;
}
area(3, "4"); // type error: the second argument is a string
Beide Parameter müssen Zahlen sein. Das geschriebene "4" ist eine Zeichenkette, daher meldet die Typprüfung die Abweichung. Einfaches JavaScript würde diese Zeichenkette bei der Multiplikation implizit umwandeln: 3 * "4" ergibt 12. Bei der Addition ergibt 3 + "4" die Zeichenkette "34". Ein Typfehler zeigt eine Abweichung von den vorgesehenen Eingaben an, auch wenn JavaScript den Ausdruck ausführen könnte.
Ein Interface beschreibt die Struktur eines Objekts:
interface Shape {
name: string;
area(): number;
}
Eine mit implements Shape deklarierte Klasse muss die angegebenen Eigenschaften und Methoden mit kompatiblen Typen bereitstellen. Die Typprüfung kann bei aktivierter strikter Prüfung auch falsch geschriebene Eigenschaften, falsche Argumentzahlen und mögliche undefined-Werte erkennen.
Trenne Typprüfung und Entfernen der Typen
| Werkzeug oder Aktion | Funktion |
|---|---|
tsc | Prüft Typen und gibt normalerweise JavaScript aus |
tsc --noEmit | Prüft Typen, ohne Ausgaben zu schreiben |
| TypeScript-Transformation von Vite | Entfernt Typsyntax für die Ausführung |
| Type Stripping von Node.js | Führt unterstützten entfernbaren TypeScript-Code nach dem Entfernen der Typen aus |
Die TypeScript-Projekteinstellungen stehen in tsconfig.json. Ein Vite-Build stellt nicht selbst fest, ob alle Typen korrekt sind. Projekte führen häufig tsc --noEmit separat aus. Aktuelles Node.js kann unterstützte .ts-Dateien direkt ausführen, prüft ihre Typen beim Type Stripping aber ebenfalls nicht.
Behalte Laufzeitprüfungen an Datengrenzen bei
Typannotationen werden vor der Ausführung entfernt. Die Deklaration des erwarteten Typs von Daten aus fetch() oder JSON.parse() überprüft den tatsächlichen Wert nicht. Prüfe externe Daten im laufenden Code, bevor du dich auf sie verlässt. Ein Server kann eine Zeichenkette senden, selbst wenn eine Typdeklaration ein Feld als numerisch bezeichnet.
private und readonly von TypeScript sind Einschränkungen der Typprüfung; sie bieten nicht den privaten Zugriffsschutz von JavaScript mit # zur Laufzeit. Private JavaScript-Felder werden weiterhin geschützt, während das Programm läuft. TypeScript macht ein Programm auch nicht schneller und beweist nicht, dass seine Berechnungen korrekt sind.
Der Typ any schaltet nützliche Prüfungen für einen Wert aus. Er kann eine Fehlermeldung unterdrücken, während der ursprüngliche Fehler bestehen bleibt. Das Korrigieren einer falschen Annahme oder das Hinzufügen einer tatsächlichen Datenprüfung liefert der Typprüfung bessere Informationen.
Führe TypeScript in überschaubaren Schritten ein
Eine JavaScript-Datei kann mit // @ts-check beginnen und JSDoc wie /** @param {number} width */ verwenden, um in unterstützenden Editoren Prüfungen zu erhalten, ohne zu einer .ts-Datei zu werden. Ein Projekt kann später Dateien einzeln umbenennen. Bibliotheken können .d.ts-Typdefinitionen bereitstellen; Definitionen der Community werden häufig unter @types/... verteilt.
Größere, langlebige Projekte erhalten klarere Schnittstellen, sichereres Refactoring und bessere Vervollständigung im Editor. Bei einem kleinen Skript rechtfertigt der Nutzen möglicherweise noch keinen weiteren Buildschritt und keine neue Syntax. Tests bleiben in beiden Fällen nützlich: Eine korrekt typisierte Flächenfunktion kann trotzdem eine falsche Zahl berechnen.
Wichtige Erkenntnisse
- TypeScript prüft erwartete Werttypen vor der Ausführung.
- Das Entfernen von Typsyntax ist von der Typprüfung getrennt.
- Typdeklarationen validieren externe Daten nicht zur Laufzeit.
- Der Zugriffsschutz mit
#in JavaScript wird zur Laufzeit durchgesetzt; TypeScript-privatewird statisch geprüft. - JSDoc-Prüfung ermöglicht eine schrittweise Einführung, bevor Dateien in TypeScript umgewandelt werden.
5.2 Kombiniere Werkzeuge passend zum Bedarf des Projekts
Eine Bibliothek stellt Code bereit, den dein Programm aufruft. Ein Framework bietet eine Anwendungsstruktur und ruft deinen Code zu den richtigen Zeitpunkten auf. Diese Änderung der Steuerung hilft, größere Oberflächen zu koordinieren, beruht aber weiterhin auf JavaScript, Modulen und einer Projektwerkzeugkette.
Gliedere eine Oberfläche in Komponenten
React, Vue, Svelte und Angular gliedern Benutzeroberflächen mit Komponenten: wiederverwendbaren Teilen wie einer Navigationsleiste oder einem Aufgabeneintrag. Eine Komponente verbindet Markup und Verhalten, oft mit Stilen. Wenn sich ihr Zustand ändert, koordiniert das System für die Benutzeroberfläche die betreffenden Seitenaktualisierungen.
Meta-Frameworks wie Next.js, Nuxt, SvelteKit und Astro ergänzen eine umfangreichere Einrichtung, darunter Routing, Rendern auf dem Server und Buildunterstützung. Ihr genauer Funktionsumfang unterscheidet sich. Kenntnisse über JavaScript-Funktionen, Ereignisse, asynchronen Code und Modulgrenzen helfen beim Erlernen jedes dieser Frameworks.
Eine Seite mit wenigen unabhängigen interaktiven Funktionen kann mit einfachem JavaScript übersichtlich sein. Ein Framework wird nützlich, wenn viele Ansichten sich ändernde Daten gemeinsam nutzen müssen. Wähle es für diesen Bedarf, statt anzunehmen, dass jede Seite die größte Werkzeugkette benötigt.
Verfolge das Zusammenspiel der Werkzeuge
Framework-Pakete werden über npm installiert und als Module importiert. Buildwerkzeuge transformieren Syntax wie JSX oder Framework-spezifische Dateien. TypeScript kann Schnittstellen zwischen Teilen prüfen. Linter-Prüfung, Formatierung und Tests behalten unabhängig von der Wahl der Benutzeroberfläche ihre unterschiedlichen Aufgaben.
Starte ein einfaches Projekt und füge die vom Team verwendeten Werkzeuge hinzu:
npm create vite@latest shape-lab
cd shape-lab
npm install
npm install --save-dev eslint @eslint/js globals prettier vitest
Eine Vite-Vorlage stellt Entwicklungs- und Buildwerkzeuge bereit. Die Installation zusätzlicher Pakete konfiguriert sie nicht: Füge die ESLint-Konfiguration hinzu, erstelle Tests und richte Editor-Erweiterungen für Linter-Prüfung und Formatierung beim Speichern ein. Ein Feld für Skripte kann diese Aufgaben bereitstellen:
{
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"lint": "eslint .",
"format": "prettier . --write",
"test": "vitest run"
}
Diese Einträge gehören in "scripts" in package.json. Führe während der Bearbeitung npm run dev aus; der Editor kann bei Dateiänderungen prüfen und formatieren. Führe vor dem Weitergeben der Arbeit npm test aus. Führe vor der Veröffentlichung npm run build und npm run preview aus. Der gebaute Ordner dist einer statischen Website kann anschließend zu einem geeigneten statischen Hoster hochgeladen werden.
Finde die Datei, die eine Aufgabe steuert
| Datei oder Ordner | Aufgabe |
|---|---|
package.json | Befehle und Bereiche direkter Abhängigkeiten |
package-lock.json | Genauer Abhängigkeitsbaum |
node_modules/ | Generierte installierte Pakete |
eslint.config.js | Linter-Regeln und Umgebungen |
.prettierrc | Formatierungsoptionen |
vite.config.js | Entwicklungs- und Buildoptionen |
tsconfig.json | Einstellungen für TypeScript-Prüfung und Kompilierung |
dist/ | Generierte Buildausgabe |
Beginne bei einem unbekannten Projekt mit package.json: Die Skripte zeigen, wie das Projekt ausgeführt wird, und die Abhängigkeitslisten zeigen, welche Werkzeuge diese Skripte benötigen. Generierte Ordner können neu erstellt werden; Einstellungen und Quellcode erklären, wie das geht.
Lerne neue Werkzeuge einzeln kennen, damit ein Fehler noch eine erkennbare Ursache hat. Nutze die offizielle Dokumentation für die installierte Version, wenn Befehle oder Standardeinstellungen abweichen, denn Veröffentlichungen von Werkzeugen und Frameworks ändern sich häufiger als die JavaScript-Kernsyntax.
Wichtige Erkenntnisse
- Dein Code ruft eine Bibliothek auf; ein Framework ruft deinen Code innerhalb seiner Struktur auf.
- Komponenten koordinieren wiederverwendbare Teile der Benutzeroberfläche und sich ändernden Zustand.
- Framework-Projekte hängen weiterhin von Modulen, Paketen, Builds und Prüfungen ab.
- Vorlagen und Paketinstallationen konfigurieren nicht jedes zusätzliche Werkzeug automatisch.
- Lies zuerst die Paketskripte, um den Arbeitsablauf eines Projekts kennenzulernen.
- Füge Werkzeuge für einen konkreten Bedarf hinzu und prüfe die Dokumentation der installierten Version.
Klassen und Module in JavaScript: Beispiele
1Erstelle eine kleine Familie von Objekten
18 Schritte
- 1EinführungBeginne mit Objekten, die Verhalten benötigen
Ein Programm kann viele Rechtecke speichern, aber jedes benötigt auch Operationen wie Fläche und Umfang. Eine Klasse ermöglicht diesen Objekten, die Operationen gemeinsam zu nutzen und ihre Maße getrennt zu halten.
Verfolge das Objekt von seiner Erstellung bis zu einem Methodenaufruf. Ergänze dann geschützten Zustand, unterscheide Werte auf Klassenebene und spezialisiere Verhalten durch Vererbung. Untersuche schließlich die Prototypenkette, um zu sehen, wo die gemeinsam genutzten Methoden liegen.
- 2BeispielInitialisiere eine Instanz
Der Konstruktor speichert Werte auf dem neuen Objekt. Hier sind die Maße in Zentimetern angegeben:
class Rectangle { constructor(width, height) { this.width = width; this.height = height; } area() { return this.width * this.height; } } const a = new Rectangle(3, 4);newerstelltaund führt den Konstruktor aus, wobeithisdarauf verweist. Die beiden Zuweisungen gebenaseine eigene Breite und Höhe. - 3BeispielRufe gemeinsames Verhalten mit getrennten Daten auf
Füge
const b = new Rectangle(5, 2);hinzu. Beide Objekte könnenareaaufrufen, aber jeder Aufruf liest die Maße des Empfängerobjekts.a.area()multipliziert 3 mit 4 und gibt 12 cm² zurück.b.area()multipliziert 5 mit 2 und gibt 10 cm² zurück. Die Zuweisungb.width = 8ändert nurb, sodass seine nächste Fläche 16 cm² beträgt, währendabei 12 cm² bleibt.Die Methode benötigt keine separate Kopie ihres Codes für jedes Rechteck. Die Daten, die sie liest, stammen von
this. - 4TippBehalte das Objekt in einem Callback
Eine von einem Objekt getrennte Methode behält ihr Empfängerobjekt nicht dauerhaft:
const calculate = a.area; // calculate(); // this is undefined const calculateSafely = () => a.area();Klassenmethoden laufen im strikten Modus. Der losgelöste Aufruf hat kein Objekt vor dem Punkt, daher schlägt das Lesen von
this.widthfehl. Der Pfeilfunktions-Wrapper erhält den ursprünglichen Methodenaufruf.a.area.bind(a)ist eine weitere Möglichkeit, einen anagebundenen Callback zu erzeugen. - 5HerausforderungSage einen zweiten Aufruf voraus
Ein neues
Rectangle(6, 2)wirdtilezugewiesen. Die Anweisungtile.width = 7wird ausgeführt. Was gibttile.area()zurück und warum ändert das kein anderes zuvor erstelltes Rechteck?---reveal---
Die Methode gibt 14 cm² zurück: Sie liest die aktuelle Breite des Empfängerobjekts, 7 cm, und seine Höhe, 2 cm. Jede Instanz speichert ihre eigenen Maße, sodass eine Änderung von
tile.widthkeine andere Instanz aktualisiert. Eine gemeinsam genutzte Methode macht die Instanzfelder nicht zu gemeinsam genutzten Feldern. - 6ErklärungSpeichere Zustand hinter einem Setter
Ein Thermostat kann
targetöffentlich zugänglich machen und#targetprivat speichern. Ein Getter liest den privaten Wert; ein Setter prüft eine angeforderte Änderung.Der Setter lehnt nicht endliche oder nicht numerische Eingaben und Werte außerhalb von 10 bis 30 °C ab, bevor er
this.#target = valuezuweist. Diese Reihenfolge ist wichtig: Wird vor der Zuweisung eine Ausnahme ausgelöst, bleibt der letzte gültige Sollwert erhalten. Der separate Speichername verhindert außerdem, dass der Setter sich wiederholt selbst aufruft. - 7BeispielVerfolge eine akzeptierte und eine abgelehnte Änderung
Beginne mit einem Thermostat-Sollwert von 20 °C. Die Zuweisung von 22 besteht beide Prüfungen, sodass der Setter 22 speichert. Die anschließende Zuweisung von 35 löst einen
RangeErroraus; der gespeicherte Wert bleibt 22.Number.isFinite(value)lehnt auch"22",NaNund Unendlichkeit ab. Die Bereichsvergleiche allein würden nicht jeden ungültigen Typ oder Wert ablehnen. Der private Speicher steuert den direkten Zugriff, während die Validierung steuert, was die öffentliche Schnittstelle akzeptiert. - 8KernaussageWähle zwischen Klassen- und Instanzspeicher
Ein mit
staticdeklariertes Feld gehört zur Klasse. Lies die untere Schranke des Thermostats alsThermostat.MIN, währendt.targetden Zustand einer Instanz liest.Ein statischer Zähler kann erfassen, wie viele Objekte erstellt wurden. Ein normales Feld beginnt dagegen auf jedem neuen Objekt separat. Innerhalb einer statischen Methode ist
thisdie Klasse, die den Aufruf empfängt, und stellt daher nicht den Sollwert oder die Maße einer einzelnen Instanz bereit. - 9HerausforderungNutze den öffentlichen Zugriff
Eine Unterklasse von
Thermostatmuss ihren Sollwert von 20 auf 24 °C erhöhen. Die Oberklasse deklariert#targetund einen validierenden öffentlichen Settertarget. Wie sollte die Unterklasse die Änderung anfordern und was würde ein direkter Zugriff überthis.#targetbewirken?---reveal---
Weise
this.target = 24zu, damit der geerbte Setter den Wert validiert und speichert. Die Unterklasse kann das private Feld der Oberklasse nicht direkt benennen; diese Verwendung von#targetist ein Syntaxfehler. Geerbte Zugriffsmethoden laufen weiterhin in der Klasse, die den privaten Zustand deklariert hat. - 10BeispielGib verwandten Klassen eine Oberklasse
Ein Konstruktor von
Shapespeichert einen Namen und seine Methodedescribe()ruftthis.area()auf. Ein Kreis spezialisiert die Berechnung:class Circle extends Shape { constructor(radius) { super("Circle"); this.radius = radius; } area() { return Math.PI * this.radius ** 2; } }superinitialisiert den Teil der Oberklasse, bevorthis.radiusverwendet wird. Ein Kreis ist eine Form, daher passt die geerbte Beschreibung zu seiner Rolle. - 11BeispielLass das Empfängerobjekt die Berechnung wählen
Bei einem Radius von 2 cm gibt
new Circle(2).describe()den Wert"Circle with area 12.57 cm²"zurück. Die Beschreibungsmethode ist geerbt, aber ihr Aufrufthis.area()findet die überschreibende Methode des Kreises.Ein Rechteck kann dieselbe Beschreibung erben und die Multiplikation als eigene Flächenmethode bereitstellen. Eine Schleife, die
describe()auf beiden Objekten aufruft, benötigt keine separaten Ausführungszweige für Kreis und Rechteck. Diese gemeinsame Schnittstelle mit unterschiedlichem Verhalten ist Polymorphie. - 12ErklärungErweitere eine Methode der Oberklasse
Ein Quadrat kann in seinem Konstruktor
super(side, side)aufrufen, um einer Rechteck-Oberklasse gleiche Maße zu geben. Eine überschreibende Beschreibung kann dannsuper.describe()aufrufen und die Seitenlänge anhängen.super(...)im Konstruktor undsuper.describe()in einer Methode haben unterschiedliche Aufgaben: Das eine initialisiert die Oberklasse, das andere ruft eine Methode der Oberklasse auf. Eine Unterklasse ohne eigenen Konstruktor gibt Argumente automatisch weiter. - 13HerausforderungWähle ein sinnvolles Modell
Ein
Report-Objekt muss Nachrichten über einenNotifiersenden. Berichte sind keine Benachrichtigungsdienste. Wie sollten die Objekte miteinander verbunden sein und wie würde der Bericht eine Benachrichtigung anfordern?---reveal---
Verwende Komposition: Gib dem Bericht eine Eigenschaft für den Benachrichtigungsdienst und rufe dann eine Methode wie
this.notifier.send(message)auf. Das ist eine „hat ein“-Beziehung. WerNotifiererweitert, würde allein zur Wiederverwendung des Codes behaupten, dass jeder Bericht selbst ein Benachrichtigungsdienst ist. Das passt nicht zum Modell. - 14ErklärungFinde eine Methode über die Kette
Die eigenen Eigenschaften eines Kreises enthalten Daten wie seinen Radius. Fehlt ihm ein eigenes
describe, durchsucht JavaScript zuerstCircle.prototypeund dannShape.prototype, wo die Methode gespeichert ist.Die gewöhnliche Kette führt weiter über
Object.prototypebis zunull. Hat kein Objekt in der Kette eine angeforderte Eigenschaft, ergibt das Lesenundefined.instanceof Shapeist normalerweise erfolgreich, weilShape.prototypein der Kette der Instanz vorkommt. - 15BeispielVerdecke eine geerbte Einstellung
Erstelle
settingsmitObject.create(defaults), wobeidefaults.fontSizegleich 16 ist. Weisesettings.fontSize = 18zu.Die Zuweisung erstellt eine eigene Dateneigenschaft, die die geerbte verdeckt. Der Standardwert bleibt 16. Das Löschen von
settings.fontSizeentfernt die Verdeckung, sodass der nächste Lesezugriff wieder 16 auf dem Prototyp findet. Dieser Ablauf setzt eine gewöhnliche Dateneigenschaft voraus; stattdessen kann ein geerbter Setter eine Zuweisung behandeln. - 16HerausforderungPrüfe eigene Eigenschaften getrennt vom Zugriff
Ein Objekt
panelerbttheme: "light"von einem einfachen Prototyp und hat kein eigenestheme. Was ergebenpanel.theme,Object.hasOwn(panel, "theme")und"theme" in panel?---reveal---
Sie ergeben
"light",falseundtrue. Lesen undinkönnen geerbte Eigenschaften finden, währendObject.hasOwnnur danach fragt, was aufpanelselbst gespeichert ist. Ein erfolgreicher Lesezugriff belegt nicht, dass es eine eigene Eigenschaft ist. AuchObject.keys(panel)lässt diesen geerbten Schlüssel weg. - 17HerausforderungVerbinde Vererbung, privaten Zugriffsschutz und Suche
Sensordeklariert#value, einen öffentlichen Gettervalueund eine Methodedescribe(), diethis.valueliest.IndoorSensor extends Sensorfügt keine Mitglieder hinzu. Warum kann ein Innensensordescribe()aufrufen, obwohl sein Klassenrumpf#valuenicht benennen kann?---reveal---
Die Suche findet die geerbte Methode, die mit dem Innensensor als
thisausgeführt wird. Der inSensordeklarierte Getter kann sein eigenes privates Feld auf dieser Instanz lesen. Die Vererbung stellt das öffentliche Verhalten bereit, ohne der Unterklasse direkten Zugriff auf den privaten Namen der Oberklasse zu gewähren. - 18ZusammenfassungUntersuche ein Objekt in einer sinnvollen Reihenfolge
Frag dich beim Verfolgen einer Klasseninstanz, wo ihre Daten gespeichert sind, welches Objekt den Methodenaufruf empfängt und wo die Suche die Methode findet. Läuft eine Änderung über einen Setter, verfolge die Validierung vor der Zuweisung.
Untersuche bei einer unbekannten Hierarchie
extendsund die Konstruktorargumente, bevor du überschreibende Methoden verfolgst. Prüfe, welche Schlüssel dem Objekt selbst gehören, wenn du gespeicherte Schlüssel benötigst, und folge den Prototypen, wenn du geerbtes Verhalten erklären möchtest.
2Verbinde ein Programm über Modulgrenzen hinweg
13 Schritte
Speichere diese Einheit, um jede Interaktive Lektion Schritt für Schritt durchzugehen.
- 1EinführungVerfolge einen Abhängigkeitsgraphen
- 2BeispielExportiere die Berechnung
- 3BeispielExportiere und nutze die Klasse
- 4HerausforderungLies einen geänderten Export
- 5BeispielStarte die Dateien in einem Browser
- 6ErklärungLöse einen Paketnamen auf
- 7HerausforderungWähle das Node.js-Ladesystem
- 8BeispielLade ein optionales Diagramm später
- 9KernaussageErmittle, was auf die Einrichtung wartet
- 10HerausforderungLöse einen unnötigen Zyklus auf
- 11TippHalte Arbeit aus Importen heraus
- 12HerausforderungVerbinde das Laden mit einer klaren Schnittstelle
- 13ZusammenfassungVerfolge einen Modulfehler nach Phasen
3Stelle ein npm-Projekt auf einem anderen Computer wieder her
11 Schritte
Speichere diese Einheit, um jede Interaktive Lektion Schritt für Schritt durchzugehen.
- 1EinführungMache das Projekt reproduzierbar
- 2BeispielErstelle die Paketbeschreibung
- 3BeispielLies die Aufgabe vor ihrer Ausführung
- 4HerausforderungBestimme eine Versionsänderung
- 5ErklärungHalte Code und Werkzeuge getrennt fest
- 6BeispielLies, was ein Bereich erlaubt
- 7ErklärungInstalliere den festgehaltenen Baum
- 8HerausforderungDeute eine gescheiterte saubere Installation
- 9TippPlane eine bewusste Aktualisierung
- 10HerausforderungVerbinde einen Bereich mit einem sauberen Build
- 11ZusammenfassungLies die beiden Paketeinträge zusammen
4Prüfe ein Projekt von der Bearbeitung bis zum Build
15 Schritte
Speichere diese Einheit, um jede Interaktive Lektion Schritt für Schritt durchzugehen.
- 1EinführungStelle jedem Werkzeug eine andere Frage
- 2ErklärungKonfiguriere einen hilfreichen Linter-Bericht
- 3BeispielFinde einen Tippfehler vor der Ausführung
- 4HerausforderungWähle eine Formatierungsprüfung
- 5BeispielFühre Entwicklung, Build und Vorschau aus
- 6BeispielVerfolge eine Buildoptimierung
- 7VerbindungVerbinde die Ausgabe mit dem Quellcode
- 8HerausforderungEntscheide, was neu gebaut werden muss
- 9BeispielSchreibe eine unabhängige Erwartung
- 10KernaussageVergleiche Inhalt und Identität richtig
- 11HerausforderungTeste den genauen Grenzwert
- 12TippWarte in einem asynchronen Test
- 13BeispielVerwende eine sinnvolle numerische Toleranz
- 14HerausforderungUntersuche einen erfolgreichen Build mit falschem Ergebnis
- 15ZusammenfassungLies Fehlschläge als Belege
5Ordne Typen und Komponenten in ein funktionierendes Projekt ein
10 Schritte
Speichere diese Einheit, um jede Interaktive Lektion Schritt für Schritt durchzugehen.
- 1EinführungFüge ein Werkzeug für einen bestimmten Bedarf hinzu
- 2BeispielErkenne einen unpassenden Aufruf
- 3ErklärungTrenne Transformation und Prüfung
- 4HerausforderungPrüfe Daten, die zur Laufzeit eintreffen
- 5ErklärungOrdne eine Komponente in den Arbeitsablauf ein
- 6BeispielLies das Aufgabenmenü des Projekts
- 7HerausforderungEntscheide, wann ein Framework hilft
- 8HerausforderungFinde die fehlende Prüfung
- 9TippFühre Prüfungen schrittweise ein
- 10ZusammenfassungVerfolge, welche Belege jedes Werkzeug liefert
Klassen und Module in JavaScript: Übungsaufgaben
Set 1: Lies Klassen, Importe und Projektaufgaben10 Übungsfragen
Frage 1 von 10
LIMIT als benannten Export. Wie kann eine Deklaration die Funktion als calculate und die Konstante unter ihrem exportierten Namen importieren?Antwort zeigenAntwort ausblenden
{ calculate, LIMIT } fragt nach zwei benannten Exporten, aber die Funktion wurde als Standardexport bereitgestellt. Der lokale Name calculate wird vom Importeur gewählt und muss nicht mit dem Deklarationsnamen der Funktion übereinstimmen.Frage 2 von 10
static created = 8;. Ihre Instanz wird batch zugewiesen und die Klasse heißt Batch. Wie wird der gespeicherte Zählerstand gelesen?Antwort zeigenAntwort ausblenden
Batch.created gelesen. Der Zugriff auf batch.created durchsucht die Instanz und ihre Prototypenkette, auf der dieses statische Feld nicht abgelegt wurde. Statisch bedeutet nicht, dass ein Feld auf jedes Objekt kopiert wird.Frage 3 von 10
Warum schlägt dieser Konstruktor fehl und welche Reihenfolge behebt das Problem?
class Item {
constructor(code) { this.code = code; }
}
class Box extends Item {
constructor(code) {
this.open = false;
super(code);
}
}
new Box("K7");
Antwort zeigenAntwort ausblenden
this erst verwenden, nachdem der Konstruktor der Oberklasse die Instanz über super(code) initialisiert hat. Verschiebe diesen Aufruf vor die Zuweisung an open. Einem anderen Feld zuerst einen Wert zuzuweisen, hätte dasselbe Problem, weil weiterhin zu früh auf this zugegriffen würde.Frage 4 von 10
export default class Badge {}. Welcher Import bindet diese Klasse an den lokalen Namen Tag?Antwort zeigenAntwort ausblenden
Tag wählen. Der Import von { Badge as Tag } fordert dagegen einen benannten Export namens Badge an, den dieses Modul nicht bereitstellt. Ein Namensraumimport würde ein Objekt binden, das den Standardexport enthält, nicht die Klasse selbst.Frage 5 von 10
import-Deklaration, aber ihr script-Tag hat kein type-Attribut. Welche HTML-Änderung macht sie zu einem ES-Modul?Antwort zeigenAntwort ausblenden
type="module" weist den Browser an, ES-Modulsyntax und die zugehörigen Laderegeln zu verwenden. Nur defer hinzuzufügen, ändert den Ausführungszeitpunkt eines klassischen Skripts, macht Importdeklarationen darin aber nicht gültig. Das Einstiegsmodul kann seine Abhängigkeiten dann automatisch laden, wenn die Dateien korrekt bereitgestellt werden.Frage 6 von 10
"scripts": { "check": "eslint ." }. Welcher Befehl führt diese benannte Aufgabe aus?Antwort zeigenAntwort ausblenden
npm run ausgeführt, diese Aufgabe also mit npm run check. npm check wird nicht zur Kurzform, nur weil ein Skript so heißt; die bekannten Formen npm test und npm start sind Sonderfälle. Das Skript führt den festgelegten ESLint-Befehl aus.Frage 7 von 10
Antwort zeigenAntwort ausblenden
devDependencies und wird normalerweise mit npm install -D hinzugefügt. Der Skriptabschnitt kann einen Befehl enthalten, der ihn ausführt, hält damit aber nicht die Paketabhängigkeit fest. Bibliothekscode für die Laufzeit gehört in dependencies.Frage 8 von 10
Antwort zeigenAntwort ausblenden
Frage 9 von 10
"a" und "b" zurück. Wie sollte ein Vitest-Test diesen Inhalt mit einem separaten Arrayliteral vergleichen?Antwort zeigenAntwort ausblenden
toEqual prüft den Inhalt des Arrays einschließlich der Reihenfolge seiner Werte. toBe und die Identitätsausdrücke vergleichen, ob beide Operanden dasselbe Objekt sind; ein separates Arrayliteral erstellt ein anderes Objekt. Gleicher Inhalt erfordert daher den gezeigten tiefen Vergleich.Frage 10 von 10
Antwort zeigenAntwort ausblenden
Set 2: Wende Module und reproduzierbare Projekteinstellungen an10 Übungsfragen
Speichere diese Einheit, um jede Antwort zu sehen und jedes Set zu üben.
Frage 1 von 10
Was geschieht in der letzten Zeile?
class Meter {
value = 2;
read() { return this.value; }
}
const meter = new Meter();
const read = meter.read;
read();
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 2 von 10
Was ist das letzte Ergebnis?
const base = { unit: "cm" };
const sample = Object.create(base);
Object.hasOwn(sample, "unit");
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 3 von 10
Was gibt der letzte Ausdruck zurück?
class Label {
constructor(text) { this.text = text; }
length() { return this.text.length; }
}
new Label("Orbit").length();
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 4 von 10
import("./help.js") unmittelbar zurück?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 5 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 6 von 10
~6.4.2. Welche ist die neueste erlaubte stabile Version unter 6.4.9, 6.5.0, 7.0.0 und 6.4.1?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 7 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 8 von 10
loadItems() auf, ohne darauf zu warten. Der Test-Callback endet; das Promise wird später mit einem neuen Array erfüllt. Die vorgesehene Assertion verwendet toBe(["x"]). Was muss sich ändern, damit der Test den versprochenen Inhalt zuverlässig prüft?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 9 von 10
.ts-Programm läuft in Node.js durch unterstütztes Type Stripping. Warum belegt dieser erfolgreiche Lauf nicht, dass alle deklarierten Typen korrekt sind?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 10 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Set 3: Verfolge Zustand, Abhängigkeiten und Buildverhalten10 Übungsfragen
Speichere diese Einheit, um jede Antwort zu sehen und jedes Set zu üben.
Frage 1 von 10
"8" zuzuweisen, erhalten und warum?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 2 von 10
Was sind die abschließend gelesenen Werte?
const base = { value: 4, read() { return this.value; } };
const x = Object.create(base);
const y = Object.create(x);
x.value = 7;
y.value = 10;
delete y.value;
[y.read(), Object.hasOwn(y, "read"), base.value];
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 3 von 10
Was sind die letzten beiden Werte?
const defaults = { size: 11 };
const item = Object.create(defaults);
item.size = 15;
delete item.size;
[item.size, defaults.size];
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 4 von 10
"tiny-calendar". Das Paket ist lokal installiert, aber der Browser meldet einen nicht aufgelösten Modulbezeichner. Welche fehlende Einrichtung erklärt das?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 5 von 10
main.js importiert statisch config.js, das auf eine Netzwerkantwort wartet, bevor es Einstellungen exportiert. Ein unabhängiger Einstieg clock.js importiert keine der beiden Dateien. Später führt der asynchrone Handler von main const plugin = await import("./plugin.js") aus; dessen einziger Export ist die Standardklasse Plugin. Was beschreibt das Warten und die Konstruktion korrekt?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 6 von 10
^8.1.0 und legen eine Abhängigkeit auf 8.1.3 fest. Ein sauberer Build führt npm ci aus, nachdem 8.7.0 veröffentlicht wurde. Welche Version wird installiert?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 7 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 8 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 9 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 10 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Set 4: Diagnostiziere Klassen- und Projektfehler10 Übungsfragen
Speichere diese Einheit, um jede Antwort zu sehen und jedes Set zu üben.
Frage 1 von 10
Was sind die abschließend gelesenen Werte?
class Counter {
count = 1;
add() { this.count += 2; }
}
const a = new Counter();
const b = new Counter();
const addA = a.add.bind(a);
b.count = 10;
addA();
const callB = () => b.add();
callB();
[a.count, b.count];
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 2 von 10
#level und stellt auf ihrem Prototyp einen öffentlichen Getter bereit. Sie definiert keine Methode toJSON. Was ergibt JSON.stringify(new Gauge())?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 3 von 10
class Photo extends Asset {} wird ohne Konstruktor deklariert. Asset speichert das Argument, das an seinen Konstruktor übergeben wird. Was geschieht mit einem Argument in new Photo("P8")?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 4 von 10
let count = 1, ein Objekt state = { count: 1 } und advance(), das beide Zähler erhöht. Zwei weitere Module importieren dieselben Exporte. Das erste ruft advance() auf und weist dann state.count = 9 zu. Was liest das zweite als [count, state.count] und kann es count einen neuen Wert zuweisen?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 5 von 10
save() und eine Schaltfläche verwendet inline onclick="save()". Das Modul lädt korrekt, aber beim Klicken wird gemeldet, dass save undefiniert ist. Wie sollte das Ereignis verbunden werden?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 6 von 10
"inspect": "eslint src". Welcher Befehl führt dieses Skript mit dem ESLint-Argument --fix aus?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 7 von 10
no-undef auslöst. Welche Einrichtung beseitigt den Formatierungskonflikt und erhält die nützliche Fehlerprüfung?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 8 von 10
--write direkt?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 9 von 10
Eine Prüfung muss endliche numerische Längen von 0 bis einschließlich 12 cm akzeptieren. Welche drei Testerwartungen decken unterschiedliche Fehler in dieser Implementierung auf?
function setLength(n) {
if (n <= 0 || n > 12) throw new RangeError();
return n;
}
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 10 von 10
dist in diesem Arbeitsablauf?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Set 5: Kombiniere die Konzepte in unbekannten Projekten10 Übungsfragen
Speichere diese Einheit, um jede Antwort zu sehen und jedes Set zu üben.
Frage 1 von 10
Was gibt new LoudNote("A").describe() zurück?
class Note {
constructor(text) { this.text = text; }
label() { return this.text; }
describe() { return "[" + this.label() + "]"; }
}
class LoudNote extends Note {
label() { return super.label() + "!"; }
describe() { return super.describe() + "?"; }
}
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 2 von 10
Was gibt der letzte Aufruf zurück?
class Base {
constructor(n) { this.n = n; }
value() { return this.n; }
report() { return this.value() + 1; }
}
class Double extends Base {
value() { return super.value() * 2; }
}
class Extra extends Double {
report() { return super.report() + 3; }
}
new Extra(4).report();
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 3 von 10
feature.js hat einen Standardexport und das benannte ready. Eine Barrel-Datei enthält nur export * from "./feature.js";. Was leitet die Barrel-Datei weiter?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 4 von 10
document zuzugreifen. Welche Umstrukturierung behebt beide Fehler?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 5 von 10
"private": true in package.json direkt?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 6 von 10
^0.6.2, legt 0.6.4 fest und hat eine gültige passende Lockdatei. Die Registry bietet 0.6.9 und 0.7.0 an. Welche Folge beschreibt die Version nach npm ci und danach die neueste erlaubte Version nach einem bewussten npm update?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 7 von 10
VITE_-Variablen. Warum ist diese Zugangsinformation selbst nach der Minifizierung für diesen Ort ungeeignet?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 8 von 10
RangeError auslöst. Welche Vitest-Assertion ermöglicht der Vergleichsmethode, die Ausnahme zu erfassen?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 9 von 10
private value, weist ihm geparste Netzwerkeingaben zu und gibt mit der falschen Formel ein numerisches Ergebnis zurück. Das Projekt besteht tsc --noEmit. Welche Kombination behandelt privaten Zugriffsschutz zur Laufzeit, externe Eingaben und Verhalten?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 10 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Hängst du bei deiner eigenen Aufgabe fest? Hausaufgabenhilfe
Schlüsselbegriffe
A
Abhängigkeit
Arrange–Act–Assert
Assertion
Aufzählbare Eigenschaft
Object.keys listet eigene aufzählbare Zeichenkettenschlüssel auf, während for...in auch geerbte Schlüssel einbeziehen kann.B
Barrel-Datei
Benannter Export
MAX, und ein Importeur kann den Namen direkt anfordern oder lokal umbenennen.Bibliothek
Bloßer Modulbezeichner
Bundler
C
Code-Splitting
Codeformatierer
CommonJS
require-Aufrufe und module.exports, um Werte bereitzustellen..cjs-Datei bleibt CommonJS, selbst wenn das umgebende Paket ausdrücklich ES-Module auswählt.D
Dynamischer Import
import()-Ausdruck, der ein Modul bei Bedarf lädt und ein Promise für seinen Modulnamensraum zurückgibt.E
Eigene Eigenschaft
Empfängerobjekt
this liefert.box.open() ist box das Empfängerobjekt; das Loslösen von open bindet dieses Empfängerobjekt nicht dauerhaft.Entwicklungsabhängigkeit
-D installiert, damit seine Version für das Projekt festgehalten wird.Entwicklungsserver
ES-Modul
import und export verwendet und einen eigenen Gültigkeitsbereich sowie strikte Ausführungsregeln hat.F
Flache Konfiguration
Framework
G
Geerbte Eigenschaft
Object.hasOwn meldet, dass es keinen eigenen Designschlüssel hat.Getter
thermostat.target kann einen Getter aufrufen, der den gespeicherten privaten Temperatursollwert zurückgibt.Grenzwerttest
H
Hot Module Replacement
I
Import-Map
Inhaltshash
Instanz
Instanzfeld
Interface
K
Kapselung
Klasse
Rectangle kann mehrere Formen erstellen, deren Flächenmethoden ihre eigenen Breiten und Höhen verwenden.Komponente
Konstruktor
Konstruktorfunktion
new zum Initialisieren von Objekten verwendet wird und häufig mit Methoden kombiniert wird, die ihrem Prototyp zugewiesen sind.area nach der Definition einer Konstruktorfunktion an Rectangle.prototype anhängen.L
Laufzeitvalidierung
Linter
Live-Bindung
Lockdatei
npm ci, um den festgelegten Baum auf einem anderen Computer wiederherzustellen.M
Meta-Framework
Methode
tile.area() ermöglicht der Methode, die Maße von tile über das Empfängerobjekt this zu lesen.Methodenüberschreibung
area() und erbt weiterhin die gemeinsame Beschreibungsmethode der Oberklasse.Minifizierung
Mixin
Modulbezeichner
"../utils.js" fordert vom Ladesystem ein Modul einen Ordner oberhalb der importierenden Datei an.Modulnamensraum
default, wenn ein Standardexport existiert.N
npm
npm-Skript
npm run lint ausführt.npx
Ö
Öffentliche Schnittstelle
O
Oberklasse
Objektkomposition
P
Paket
Polymorphie
describe() auf Kreisen und Rechtecken auf und jedes Objekt liefert die passende Fläche.Private Methode
# beginnt, und hält Hilfsoperationen aus der öffentlichen Schnittstelle des Objekts heraus.this.#check(value) aufrufen, um Eingaben zu validieren, ohne diese Hilfsmethode für externen Code offenzulegen.Privates Feld
# beginnt und auf den nur innerhalb der Klasse, die den Namen deklariert, direkt zugegriffen werden kann.Prototyp
null, mit dem ein anderes Objekt verknüpft ist und das durchsucht wird, wenn diesem Objekt eine angeforderte eigene Eigenschaft fehlt.Prototypenkette
null endet, wenn kein weiterer Prototyp existiert.Shape.prototype finden, nachdem die Suche Circle.prototype durchlaufen hat.R
Reexport
S
Saubere Installation
Schweregrad einer Regel
Seiteneffekt
Semantische Versionierung
Setter
Sicherheitsprüfung der Abhängigkeiten
Source Map
Standardexport
Circle importiert werden, selbst wenn eine andere Datei ihr einen anderen lokalen Namen gibt.Statische Analyse
Statische Fabrikmethode
Rectangle.square(5) anbieten, um eine Instanz mit gleichen Seitenlängen zu erstellen.Statischer Initialisierungsblock
Statisches Mitglied
T
Testgetriebene Entwicklung
Testrunner
Toleranz
Top-Level-await
Transitive Abhängigkeit
Tree Shaking
Typannotation
Typdefinition
Type Stripping
TypeScript
U
Unittest
Unterklasse
extends deklarierte Klasse, die von einer Oberklasse erbt und Mitglieder hinzufügen oder geerbtes Verhalten spezialisieren kann.V
Verdeckung
Vererbung
Versionsbereich
Verzögert ausgeführtes Skript
Z
Zustand
Zyklische Abhängigkeit
Über diese Einheit
Erstelle deine eigenen Lernmaterialien
Mach aus deinen Notizen, Folien und Dokumenten Lernleitfäden, Übungsfragen, Karteikarten und 6 weitere Tools.