Aufwärmen · Aufgabe 1 von 7
Zum Aufwärmen aus Lektion B4.4: Was gibt dieser Code aus?
function makeAdder(x) {
return (y) => x + y;
}
const add2 = makeAdder(2);
console.log(typeof add2, add2(5));// B4.5 · ca. 38 Min. · Einstieg
Nach diesem Projekt schreiben Sie Funktionsfabriken: Zähler mit eigenem privaten Stand, Formatierer, die sich ihre Einstellungen merken, und kleine Funktionen, verbunden mit pipe.
Lektion 5 von 5 in B4 Funktionen
Danach können Sie
Aufwärmen · Aufgabe 1 von 7
function makeAdder(x) {
return (y) => x + y;
}
const add2 = makeAdder(2);
console.log(typeof add2, add2(5));Vorhersagen · Aufgabe 2 von 7
let count = 0;
function makeCounter() {
return () => {
count += 1;
return count;
};
}
const a = makeCounter();
const b = makeCounter();
a();
a();
console.log(b());Üben · Aufgabe 3 von 7
function makeCounter(start = 0, step = 1) {
let count = ____;
const increment = () => {
count += step;
return count;
};
return {increment};
}
const c = makeCounter(10, 5);
console.log(c.increment());Üben · Aufgabe 4 von 7
function makeFormatter(options = {}) {
const prefix = options.prefix ?? "";
const suffix = options.suffix ?? "";
const decimals = options.decimals ?? 2;
return (amount) => `${prefix}${amount.toFixed(decimals)}${suffix}`;
}
const percent = makeFormatter({suffix: "%", decimals: 1});
console.log(percent(12.34));Üben · Aufgabe 5 von 7
const pipe = (first, second) => (value) => second(first(value));
const addOne = (n) => n + 1;
const double = (n) => n * 2;
console.log(pipe(addOne, double)(5), pipe(double, addOne)(5));Denksport · Aufgabe 6 von 7
function makeCounter(start = 0, step = 1) {
let count = start;
const increment = () => {
count += step;
return count;
};
const decrement = () => {
count -= step;
return count;
};
const value = () => count;
const reset = () => {
count = start;
return count;
};
return {increment, decrement, value, reset};
}
const c = makeCounter(10, 5);
c.increment();
c.increment();
c.decrement();
console.log(c.value(), c.reset(), c.value());Anwenden · Aufgabe 7 von 7
Prüfen Sie Ihr Ergebnis anhand dieser Liste
Lesen Sie das ausgearbeitete Beispiel und lösen Sie dann die Übungen. Ihr Code läuft in Ihrem Browser oder auf Ihrem Computer und wird nie hochgeladen.
Ausgearbeitetes Beispiel
makeCounter gibt vier Funktionen zurück, die sich einen privaten Stand teilen; zwei Zähler behalten ihre eigenen Stände. makeFormatter liest seine Einstellungen einmal und gibt einen Formatierer zurück, der sie bei jedem Aufruf verwendet. pipe verbindet withVat und euro zu einem Preisschild. Führen Sie es aus, verschieben Sie dann let count = start; aus makeCounter heraus (als let count = 0; oben in der Datei) und beobachten Sie, wie sich die beiden Zähler plötzlich einen Stand teilen.
main.js
// A small closure library: counters, formatters and pipe.
function makeCounter(start = 0, step = 1) {
let count = start;
const increment = () => {
count += step;
return count;
};
const decrement = () => {
count -= step;
return count;
};
const value = () => count;
const reset = () => {
count = start;
return count;
};
return {increment, decrement, value, reset};
}
function makeFormatter(options = {}) {
const prefix = options.prefix ?? "";
const suffix = options.suffix ?? "";
const decimals = options.decimals ?? 2;
return (amount) => `${prefix}${amount.toFixed(decimals)}${suffix}`;
}
const pipe = (first, second) => (value) => second(first(value));
const visits = makeCounter();
const tickets = makeCounter(100, 10);
visits.increment();
visits.increment();
tickets.increment();
tickets.increment();
tickets.decrement();
console.log("visits:", visits.value(), "tickets:", tickets.value());
console.log("after reset:", visits.reset(), tickets.reset());
const euro = makeFormatter({suffix: " EUR"});
const dollar = makeFormatter({prefix: "$"});
const percent = makeFormatter({suffix: "%", decimals: 1});
console.log(euro(4.5), dollar(19.9), percent(12.34));
const withVat = (net) => net * 1.19;
const priceTag = pipe(withVat, euro);
console.log(priceTag(10), priceTag(25));
Ausführen mit
node main.jsAusgabe
visits: 2 tickets: 110
after reset: 0 100
4.50 EUR $19.90 12.3%
11.90 EUR 29.75 EURTab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.
Beim ersten Ausführen lädt Ihr Browser den JavaScript-Runner herunter (bis zu 0.1 MB) und speichert ihn im Cache. Ihr Code läuft in der eigenen Engine Ihres Browsers und bleibt auf Ihrem Gerät.
Übung 1 von 3
makeCounter(start = 0, step = 1) soll {increment, decrement, value, reset} zurückgeben. Jeder Zähler beginnt bei start, bewegt sich um step, und reset geht auf start zurück und gibt den Wert zurück. Im Startcode teilen sich alle Zähler ein count oben in der Datei, und start und step werden ignoriert. Reparieren Sie das, sodass jeder Zähler seinen eigenen privaten Stand hält.
Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.
Beim ersten Ausführen lädt Ihr Browser den JavaScript-Runner herunter (bis zu 0.1 MB) und speichert ihn im Cache. Ihr Code läuft in der eigenen Engine Ihres Browsers und bleibt auf Ihrem Gerät.
Führen Sie die Tests aus: Der zweite Zähler macht dort weiter, wo der erste aufgehört hat. Wie viele count-Variablen gibt es?
Verschieben Sie die Deklaration von count in makeCounter und lassen Sie es bei start beginnen: let count = start;. Dann erzeugt jeder Aufruf sein eigenes.
Verwenden Sie step in increment und decrement und start in reset.
Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.
// Each counter must keep its own count, starting at start and moving by step.
export function makeCounter(start = 0, step = 1) {
let count = start;
const increment = () => {
count += step;
return count;
};
const decrement = () => {
count -= step;
return count;
};
const value = () => count;
const reset = () => {
count = start;
return count;
};
return {increment, decrement, value, reset};
}
Installieren Sie ECMAScript 2026 oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.
main.js
// Each counter must keep its own count, starting at start and moving by step.
let count = 0;
export function makeCounter(start = 0, step = 1) {
const increment = () => {
count += 1;
return count;
};
const decrement = () => {
count -= 1;
return count;
};
const value = () => count;
const reset = () => {
count = 0;
return count;
};
return {increment, decrement, value, reset};
}
main.test.js
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {makeCounter} from './main.js';
test('zwei Zähler behalten ihren eigenen Stand', () => {
const a = makeCounter();
const b = makeCounter();
a.increment();
a.increment();
b.increment();
assert.equal(a.value(), 2, `nach zwei Erhöhungen ist a.value() ${a.value()}, erwartet 2`);
assert.equal(b.value(), 1, `nach einer Erhöhung ist b.value() ${b.value()}, erwartet 1`);
});
test('ein Zähler beginnt bei start und bewegt sich um step', () => {
const c = makeCounter(10, 5);
assert.equal(c.value(), 10, `makeCounter(10, 5).value() ist ${c.value()}, erwartet 10`);
const up = c.increment();
assert.equal(up, 15, `das erste increment hat ${up} zurückgegeben, erwartet 15`);
});
test('decrement geht um step zurück', () => {
const c = makeCounter(10, 5);
c.decrement();
const down = c.decrement();
assert.equal(down, 0, `zwei decrement-Aufrufe von 10 um 5 haben ${down} zurückgegeben, erwartet 0`);
});
test('reset geht auf start zurück', () => {
const c = makeCounter(3);
c.increment();
c.increment();
const back = c.reset();
assert.equal(back, 3, `reset() hat ${back} zurückgegeben, erwartet 3`);
assert.equal(c.value(), 3, `nach reset() ist value() ${c.value()}, erwartet 3`);
});
package.json
{
"type": "module"
}
package.json sagt Node.js, dass die .js-Dateien Module sind; lassen Sie die Datei im Ordner.
Programm ausführen:
node main.jsPrüfungen ausführen (learnrun.js muss im selben Ordner liegen):
node --testlearnrun.js herunterladenÜbung 2 von 3
makeFormatter(options = {}) liest prefix, suffix und decimals bereits. Lassen Sie den zurückgegebenen Pfeil eine Zahl formatieren: zuerst das Präfix, dann die Zahl mit genau decimals Nachkommastellen, dann das Suffix. So ergibt makeFormatter({suffix: " EUR"})(4.5) den Wert "4.50 EUR" und makeFormatter({prefix: "$"})(19.9) den Wert "$19.90". Die Datei exportiert außerdem euro, erzeugt mit der Fabrik.
Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.
Beim ersten Ausführen lädt Ihr Browser den JavaScript-Runner herunter (bis zu 0.1 MB) und speichert ihn im Cache. Ihr Code läuft in der eigenen Engine Ihres Browsers und bleibt auf Ihrem Gerät.
Führen Sie die Tests aus: Der Formatierer gibt die Zahl unverändert zurück, ohne Präfix, Suffix oder feste Nachkommastellen.
amount.toFixed(decimals) liefert die Zahl als String mit genau so vielen Nachkommastellen.
Setzen Sie die drei Teile der Reihe nach in das Template-Literal: `${prefix}${amount.toFixed(decimals)}${suffix}`.
Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.
// Return a function that formats a number with prefix, decimals and suffix.
export function makeFormatter(options = {}) {
const prefix = options.prefix ?? "";
const suffix = options.suffix ?? "";
const decimals = options.decimals ?? 2;
return (amount) => `${prefix}${amount.toFixed(decimals)}${suffix}`;
}
export const euro = makeFormatter({suffix: " EUR"});
Installieren Sie ECMAScript 2026 oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.
main.js
// Return a function that formats a number with prefix, decimals and suffix.
export function makeFormatter(options = {}) {
const prefix = options.prefix ?? "";
const suffix = options.suffix ?? "";
const decimals = options.decimals ?? 2;
return (amount) => `${amount}`;
}
export const euro = makeFormatter({suffix: " EUR"});
main.test.js
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {makeFormatter, euro} from './main.js';
test('euro(4.5) ergibt 4.50 EUR', () => {
assert.equal(euro(4.5), '4.50 EUR', `euro(4.5) hat ${JSON.stringify(euro(4.5))} zurückgegeben, erwartet "4.50 EUR"`);
});
test('ein Präfix steht vor der Zahl', () => {
const dollar = makeFormatter({prefix: '$'});
assert.equal(dollar(19.9), '$19.90', `der $-Formatierer hat ${JSON.stringify(dollar(19.9))} zurückgegeben, erwartet "$19.90"`);
});
test('decimals legt die Zahl der Nachkommastellen fest', () => {
const percent = makeFormatter({suffix: '%', decimals: 1});
assert.equal(percent(12.34), '12.3%', `percent(12.34) hat ${JSON.stringify(percent(12.34))} zurückgegeben, erwartet "12.3%"`);
});
test('ohne Optionen: zwei Nachkommastellen, nichts drumherum', () => {
const plain = makeFormatter();
assert.equal(plain(3), '3.00', `makeFormatter()(3) hat ${JSON.stringify(plain(3))} zurückgegeben, erwartet "3.00"`);
});
package.json
{
"type": "module"
}
package.json sagt Node.js, dass die .js-Dateien Module sind; lassen Sie die Datei im Ordner.
Programm ausführen:
node main.jsPrüfungen ausführen (learnrun.js muss im selben Ordner liegen):
node --testlearnrun.js herunterladenÜbung 3 von 3
pipe(first, second) soll eine Funktion zurückgeben, die first auf ihr Argument anwendet und dann second auf dieses Ergebnis. priceTag = pipe(withVat, euro) soll dann 19 % Mehrwertsteuer aufschlagen und formatieren: priceTag(10) ergibt "11.90 EUR". Der Startcode führt die beiden Funktionen in der falschen Reihenfolge aus. Reparieren Sie pipe; lassen Sie withVat, euro und priceTag, wie sie sind.
Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.
Beim ersten Ausführen lädt Ihr Browser den JavaScript-Runner herunter (bis zu 0.1 MB) und speichert ihn im Cache. Ihr Code läuft in der eigenen Engine Ihres Browsers und bleibt auf Ihrem Gerät.
Führen Sie die Tests aus: pipe(addOne, double)(3) ergibt 7. Welche Funktion lief zuerst?
Der Wert muss in first gehen und das Ergebnis von first in second.
Vertauschen Sie die Aufrufe: (value) => second(first(value)).
Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.
// Return a function that runs first, then second on first's result.
export const pipe = (first, second) => (value) => second(first(value));
const withVat = (net) => net * 1.19;
const euro = (amount) => `${amount.toFixed(2)} EUR`;
// A price tag: add VAT, then format.
export const priceTag = pipe(withVat, euro);
Installieren Sie ECMAScript 2026 oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.
main.js
// Return a function that runs first, then second on first's result.
export const pipe = (first, second) => (value) => first(second(value));
const withVat = (net) => net * 1.19;
const euro = (amount) => `${amount.toFixed(2)} EUR`;
// A price tag: add VAT, then format.
export const priceTag = pipe(withVat, euro);
main.test.js
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {pipe, priceTag} from './main.js';
const addOne = (n) => n + 1;
const double = (n) => n * 2;
const square = (n) => n * n;
test('pipe führt zuerst first, dann second aus', () => {
const got = pipe(addOne, double)(3);
assert.equal(got, 8, `pipe(addOne, double)(3) hat ${got} zurückgegeben, erwartet 8`);
});
test('priceTag(10) ergibt 11.90 EUR', () => {
assert.equal(priceTag(10), '11.90 EUR', `priceTag(10) hat ${JSON.stringify(priceTag(10))} zurückgegeben, erwartet "11.90 EUR"`);
});
test('eine verbundene Funktion lässt sich erneut verbinden', () => {
const got = pipe(pipe(addOne, double), square)(1);
assert.equal(got, 16, `pipe(pipe(addOne, double), square)(1) hat ${got} zurückgegeben, erwartet 16`);
});
package.json
{
"type": "module"
}
package.json sagt Node.js, dass die .js-Dateien Module sind; lassen Sie die Datei im Ordner.
Programm ausführen:
node main.jsPrüfungen ausführen (learnrun.js muss im selben Ordner liegen):
node --testlearnrun.js herunterladenfunction makeCounter(start = 0) {
let count = start;
const increment = () => {
count += 1;
return count;
};
return {increment};
}
const counter = makeCounter;
console.log(counter.increment());
Was Node.js ausgibt
TypeError: counter.increment is not a functionWarum, und die Lösung
Ohne runde Klammern ist counter die Fabrik selbst, kein von ihr erzeugter Zähler. Die Fabrik hat kein eigenes increment, also ist counter.increment undefined, und der Aufruf schlägt fehl. Rufen Sie die Fabrik auf, um einen Zähler zu erzeugen: const counter = makeCounter();.
function makeCounter(start = 0) {
let count = start;
const increment = () => {
count += 1;
return count;
};
return {increment};
}
const counter = makeCounter();
counter.increment();
console.log(count);
Was Node.js ausgibt
ReferenceError: count is not definedWarum, und die Lösung
count lebt im Scope von makeCounter. Nur die darin erzeugten Funktionen erreichen es, die oberste Ebene der Datei nicht. Genau das hält es privat. Geben Sie dem Zähler eine Funktion, die es zurückgibt, etwa value: () => count, und rufen Sie counter.value() auf.
const euro = (amount) => `${amount.toFixed(2)} EUR`;
console.log(euro("4.5"));
Was Node.js ausgibt
TypeError: amount.toFixed is not a functionWarum, und die Lösung
toFixed ist eine Methode von Zahlen. "4.5" ist ein String, der kein toFixed hat, also schlägt der Aufruf fehl. Übergeben Sie eine Zahl, euro(4.5), oder wandeln Sie Text zuerst mit Number("4.5") in eine Zahl um.
JavaScript im Browser: die eigene Engine Ihres Browsers in einem abgeschotteten Worker. Syntaxfehler findet acorn 8.18.0, MIT. Lizenz und Quellcode
5 Fragen, ohne Hinweise. Ab 80 % ist die Lektion abgeschlossen.
Erledigen Sie zuerst alle Aufgaben oben, um das Abschlussquiz freizuschalten.