JavaScript Fortgeschritten

Arrow Functions in JavaScript

Arrow Functions in JavaScript

Arrow Functions (Pfeilfunktionen) sind die moderne, kompakte Syntax für Funktionen in JavaScript. Sie wurden mit ES6 eingeführt und sind heute der Standard.

Von function zu Arrow

Die Evolution einer Funktion:

// Traditionelle Funktion
function addiere(a, b) {
    return a + b;
}

// Funktionsausdruck
const addiere = function(a, b) {
    return a + b;
};

// Arrow Function
const addiere = (a, b) => {
    return a + b;
};

// Arrow Function - Kurzform
const addiere = (a, b) => a + b;

Alle vier machen das Gleiche, aber die Arrow-Syntax ist kürzer und eleganter.

Die Syntax im Detail

Basis-Syntax

// Mit Funktionskörper (geschweifte Klammern)
const funktion = (parameter) => {
    // Code hier
    return ergebnis;
};

// Kurzform (nur ein Ausdruck, implizites return)
const funktion = (parameter) => ausdruck;

Parameter-Varianten

// Keine Parameter - leere Klammern nötig
const hallo = () => "Hallo Welt!";

// Ein Parameter - Klammern optional
const verdopple = x => x * 2;
const verdopple = (x) => x * 2;  // Auch OK

// Mehrere Parameter - Klammern nötig
const addiere = (a, b) => a + b;

// Default-Parameter
const gruss = (name = "Gast") => `Hallo ${name}!`;

// Rest-Parameter
const summe = (...zahlen) => zahlen.reduce((a, b) => a + b, 0);

Rückgabe-Varianten

// Implizites return (ein Ausdruck)
const quadrat = x => x * x;
const istGerade = n => n % 2 === 0;

// Explizites return (Funktionskörper)
const berechne = (a, b) => {
    const ergebnis = a * b;
    return ergebnis;
};

// Objekt zurückgeben - Klammern nötig!
const erstellePerson = (name, alter) => ({ name, alter });

// ❌ Ohne Klammern - wird als Funktionskörper interpretiert
const falsch = (name) => { name: name };  // undefined!

Praktische Beispiele

Einfache Transformationen

// Zahlen verdoppeln
const zahlen = [1, 2, 3, 4, 5];
const verdoppelt = zahlen.map(x => x * 2);
// [2, 4, 6, 8, 10]

// Strings in Großbuchstaben
const namen = ["anna", "ben", "clara"];
const gross = namen.map(name => name.toUpperCase());
// ["ANNA", "BEN", "CLARA"]

// Objekte transformieren
const users = [
    { id: 1, name: "Max", email: "max@test.de" },
    { id: 2, name: "Lisa", email: "lisa@test.de" }
];
const emails = users.map(user => user.email);
// ["max@test.de", "lisa@test.de"]

Filtern

const zahlen = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

// Gerade Zahlen
const gerade = zahlen.filter(n => n % 2 === 0);
// [2, 4, 6, 8, 10]

// Größer als 5
const gross = zahlen.filter(n => n > 5);
// [6, 7, 8, 9, 10]

// Produkte unter 50€
const produkte = [
    { name: "Buch", preis: 15 },
    { name: "Laptop", preis: 999 },
    { name: "Stift", preis: 2 }
];
const guenstig = produkte.filter(p => p.preis < 50);
// [{ name: "Buch", preis: 15 }, { name: "Stift", preis: 2 }]

Sortieren

const zahlen = [3, 1, 4, 1, 5, 9, 2, 6];

// Aufsteigend
zahlen.sort((a, b) => a - b);
// [1, 1, 2, 3, 4, 5, 6, 9]

// Absteigend
zahlen.sort((a, b) => b - a);
// [9, 6, 5, 4, 3, 2, 1, 1]

// Nach Eigenschaft sortieren
const personen = [
    { name: "Clara", alter: 25 },
    { name: "Anna", alter: 30 },
    { name: "Ben", alter: 20 }
];

// Nach Alter
personen.sort((a, b) => a.alter - b.alter);

// Nach Name
personen.sort((a, b) => a.name.localeCompare(b.name));

Reduce

const zahlen = [1, 2, 3, 4, 5];

// Summe
const summe = zahlen.reduce((acc, curr) => acc + curr, 0);
// 15

// Maximum
const max = zahlen.reduce((a, b) => a > b ? a : b);
// 5

// Objekt bauen
const namen = ["Anna", "Ben", "Clara"];
const laengen = namen.reduce((obj, name) => {
    obj[name] = name.length;
    return obj;
}, {});
// { Anna: 4, Ben: 3, Clara: 5 }

Arrow Functions mit this

Der wichtigste Unterschied zu normalen Funktionen: Arrow Functions haben kein eigenes this.

Das Problem mit normalem this

const person = {
    name: "Max",
    hobbies: ["Lesen", "Gaming", "Sport"],

    // ❌ Problem: this ist undefined in der Callback-Funktion
    zeigeHobbies: function() {
        this.hobbies.forEach(function(hobby) {
            console.log(this.name + " mag " + hobby);
            // this.name ist undefined!
        });
    }
};

person.zeigeHobbies();
// undefined mag Lesen
// undefined mag Gaming
// undefined mag Sport

Die Lösung: Arrow Functions

const person = {
    name: "Max",
    hobbies: ["Lesen", "Gaming", "Sport"],

    // ✅ Arrow Function erbt this vom umgebenden Scope
    zeigeHobbies: function() {
        this.hobbies.forEach(hobby => {
            console.log(this.name + " mag " + hobby);
            // this zeigt auf person!
        });
    }
};

person.zeigeHobbies();
// Max mag Lesen
// Max mag Gaming
// Max mag Sport

this - Vergleich

const obj = {
    wert: 42,

    // Normale Funktion - eigenes this
    normal: function() {
        console.log(this.wert); // 42
    },

    // Arrow Function - this vom Parent
    arrow: () => {
        console.log(this.wert); // undefined (this ist window/global)
    },

    // Normale Funktion mit Arrow-Callback
    delayed: function() {
        setTimeout(() => {
            console.log(this.wert); // 42 - Arrow erbt this
        }, 1000);
    },

    // Normale Funktion mit normalem Callback
    delayedProblem: function() {
        setTimeout(function() {
            console.log(this.wert); // undefined - eigenes this
        }, 1000);
    }
};

Wann normale Funktionen verwenden?

Als Objekt-Methoden

// ❌ Arrow Function als Methode - this funktioniert nicht
const person = {
    name: "Max",
    gruss: () => {
        console.log(`Hallo, ich bin ${this.name}`);
        // this.name ist undefined!
    }
};

// ✅ Normale Funktion als Methode
const person = {
    name: "Max",
    gruss() {
        console.log(`Hallo, ich bin ${this.name}`);
    }
};

Als Konstruktor

// ❌ Arrow Function kann nicht als Konstruktor verwendet werden
const Person = (name) => {
    this.name = name;
};
// new Person("Max"); // TypeError!

// ✅ Normale Funktion als Konstruktor
function Person(name) {
    this.name = name;
}
new Person("Max"); // OK

Mit arguments-Objekt

// ❌ Arrow Function hat kein arguments
const arrow = () => {
    console.log(arguments); // ReferenceError!
};

// ✅ Normale Funktion hat arguments
function normal() {
    console.log(arguments); // OK
}

// ✅ Oder Rest-Parameter verwenden
const mitRest = (...args) => {
    console.log(args); // OK
};

Event Handler

// ✅ Arrow Function für einfache Handler
button.addEventListener("click", () => {
    console.log("Geklickt!");
});

// ⚠️ Aber: this zeigt nicht auf das Element!
button.addEventListener("click", () => {
    console.log(this); // window, nicht button!
});

// ✅ Wenn du das Element brauchst: event.target
button.addEventListener("click", (event) => {
    console.log(event.target); // Das Button-Element
});

// ✅ Oder normale Funktion wenn this nötig
button.addEventListener("click", function() {
    console.log(this); // Das Button-Element
});

Sofort ausführen (IIFE)

// Arrow Function IIFE
(() => {
    const privat = "Geheim";
    console.log("Sofort ausgeführt!");
})();

// Mit Parameter
((name) => {
    console.log(`Hallo ${name}!`);
})("Welt");

Verkettung und Composition

const zahlen = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

// Methoden verketten
const ergebnis = zahlen
    .filter(n => n % 2 === 0)      // Gerade: [2,4,6,8,10]
    .map(n => n * n)               // Quadriert: [4,16,36,64,100]
    .filter(n => n > 20)           // > 20: [36,64,100]
    .reduce((a, b) => a + b, 0);   // Summe: 200

console.log(ergebnis); // 200

Currying mit Arrow Functions

// Ohne Currying
const addiere = (a, b) => a + b;
addiere(2, 3); // 5

// Mit Currying - Funktion gibt Funktion zurück
const addiere = a => b => a + b;

const addiereFuenf = addiere(5);
addiereFuenf(3); // 8
addiereFuenf(10); // 15

// Direkt aufrufen
addiere(2)(3); // 5

// Praktisches Beispiel: Logger
const log = prefix => message => console.log(`[${prefix}] ${message}`);

const errorLog = log("ERROR");
const infoLog = log("INFO");

errorLog("Verbindung fehlgeschlagen"); // [ERROR] Verbindung fehlgeschlagen
infoLog("Server gestartet");           // [INFO] Server gestartet

Best Practices

1. Kurzform für einfache Ausdrücke

// ✅ Gut
const verdopple = x => x * 2;
const istGerade = n => n % 2 === 0;

// ❌ Unnötig verbose
const verdopple = (x) => { return x * 2; };

2. Klammern bei Objekten

// ✅ Gut - Klammern um Objekt
const erstelle = (name, alter) => ({ name, alter });

// ❌ Fehler
const erstelle = (name, alter) => { name, alter };

3. Konsistente Parameterklammern

// Konsistent mit Klammern (empfohlen für Teams)
const a = (x) => x * 2;
const b = (x, y) => x + y;
const c = () => "hallo";

// Oder konsistent minimalistisch
const a = x => x * 2;
const b = (x, y) => x + y;
const c = () => "hallo";

Zusammenfassung

FeatureArrow FunctionNormale Funktion
Syntax() => {}function() {}
thisVom Parent geerbtEigenes this
argumentsNicht verfügbarVerfügbar
Konstruktor
Methoden⚠️ Problematisch
Callbacks✅ Ideal⚠️ this-Probleme
// Faustregeln:
// → Callbacks, map/filter/reduce → Arrow
// → Objekt-Methoden → Normale Funktion
// → Konstruktoren → Normale Funktion
// → Alles andere → Arrow (kürzer, moderner)

Übung: Schreibe eine Funktion pipe die mehrere Funktionen verkettet: pipe(addOne, double, square)(5) sollte ((5+1)*2)^2 = 144 ergeben.