Arrow Functions sind seit ES2015 die kompakteste Form, eine Funktion in JavaScript zu schreiben — (a, b) => a + b ist eine vollwertige Funktion. Aber Arrows sind keine bloße Kurzschreibweise: sie haben kein eigenes this, kein arguments-Objekt, keinen prototype, und können nicht mit new aufgerufen werden. Diese Unterschiede machen sie zur idealen Wahl für Callbacks innerhalb von Methoden — und zur falschen Wahl für Object-Methoden, die this brauchen. Dieser Artikel zeigt die Syntax-Varianten, klärt die Unterschiede zu klassischen Funktionen und nennt die Stellen, an denen Arrows nicht passen.
Syntax-Varianten
Arrow Functions sind in mehreren Schreibweisen erlaubt, je nach Anzahl der Parameter und Body-Form.
// Ohne Parameter: leere Klammern Pflicht
const ping = () => 'pong';
console.log(ping());
// Ein Parameter: Klammern optional
const verdoppeln = x => x * 2;
console.log(verdoppeln(5));
// Mehrere Parameter: Klammern Pflicht
const summe = (a, b) => a + b;
console.log(summe(2, 3));
// Mehr als ein Statement: geschweifte Klammern + explicit return
const beschreiben = (name, alter) => {
const text = `${name} ist ${alter} Jahre`;
return text;
};
console.log(beschreiben('Anna', 30));
// Object-Literal zurückgeben: in Klammern wickeln
const erstelleUser = name => ({ name, aktiv: true });
console.log(erstelleUser('Bob'));pong
10
5
Anna ist 30 Jahre
{ name: 'Bob', aktiv: true }Die letzte Variante ist eine berüchtigte Falle: name => { name } interpretiert die Engine als Block mit Label name, nicht als Objekt-Literal — Rückgabe undefined. Die Klammer-Wicklung name => ({ name }) macht es eindeutig zur Expression.
Lexical this — kein eigenes Binding
Der wichtigste Unterschied zu klassischen Funktionen: Arrow Functions haben kein eigenes this. this zeigt auf das, was im umgebenden Scope galt — dort, wo die Arrow Function geschrieben wurde.
const obj = {
wert: 42,
klassisch: function() {
setTimeout(function() {
console.log('classic:', this.wert); // undefined — this ist global/undefined
}, 0);
},
arrow: function() {
setTimeout(() => {
console.log('arrow: ', this.wert); // 42 — this kommt von arrow()
}, 0);
},
};
obj.klassisch();
obj.arrow();classic: undefined
arrow: 42Das macht Arrows zur idealen Wahl für Inner-Callbacks innerhalb von Methoden — sie behalten den this-Bezug der äußeren Methode, ohne dass bind(this) oder const self = this nötig wäre.
Kein arguments-Objekt
Klassische Funktionen haben das implizite arguments-Objekt (Array-like mit allen übergebenen Argumenten). Arrows haben das nicht — der Versuch, arguments darin zu lesen, greift auf das arguments der umgebenden Funktion zurück (oder wirft ReferenceError, wenn keines existiert).
function klassisch() {
console.log(arguments.length, [...arguments]);
}
klassisch(1, 2, 3); // 3 [ 1, 2, 3 ]
// Arrow: arguments existiert nicht
const arrow = () => {
// console.log(arguments); // ReferenceError im Module-Scope
};
// Alternative: Rest-Parameter
const restArrow = (...args) => {
console.log(args.length, args);
};
restArrow(1, 2, 3); // 3 [ 1, 2, 3 ]3 [ 1, 2, 3 ]
3 [ 1, 2, 3 ]In der Praxis ist das selten ein Nachteil — Rest-Parameter (...args) sind ohnehin der modernere Weg, variadic Funktionen zu schreiben. Sie liefern ein echtes Array, nicht ein Array-like-Object.
Kein new möglich
Arrow Functions sind nicht konstruierbar. new arrow() wirft TypeError. Sie haben auch keinen prototype-Property.
const Tier = (name) => { this.name = name; };
try {
const t = new Tier('Bello');
} catch (e) {
console.log('Fehler:', e.message);
}
// Klassische Funktion ist konstruierbar
function TierK(name) { this.name = name; }
const k = new TierK('Bello');
console.log(k.name);
console.log('Arrow prototype:', Tier.prototype); // undefined
console.log('Function prototype:', TierK.prototype); // {} (existiert)Fehler: Tier is not a constructor
Bello
Arrow prototype: undefined
Function prototype: {}Für Constructor-Funktionen: klassische Form oder class. Arrows sind nur für „normale" Funktionen gedacht.
Object-Methoden — Arrow ist meist FALSCH
Wer eine Methode auf einem Object definiert und Zugriff auf das Objekt via this braucht, sollte keine Arrow Function nutzen.
// FALSCH: Arrow als Methode — this zeigt nicht auf obj
const obj1 = {
name: 'Anna',
sagHallo: () => {
console.log('Hallo,', this?.name); // undefined
},
};
obj1.sagHallo();
// RICHTIG: Methoden-Shorthand
const obj2 = {
name: 'Anna',
sagHallo() {
console.log('Hallo,', this.name); // 'Anna'
},
};
obj2.sagHallo();Hallo, undefined
Hallo, AnnaDas ist eine der häufigsten Anfänger-Fehler. Faustregel: Methoden-Shorthand im Object-Literal, Arrow für freie Callback-Funktionen.
Event-Handler — Arrow oft FALSCH
DOM-Event-Handler erwarten typischerweise, dass this auf das Element zeigt. Mit Arrow geht das nicht.
// FALSCH: this zeigt NICHT auf button
button.addEventListener('click', () => {
this.classList.add('aktiv'); // this ist nicht button
});
// RICHTIG: klassische Funktion
button.addEventListener('click', function() {
this.classList.add('aktiv'); // this = button
});
// ODER: e.currentTarget statt this
button.addEventListener('click', (e) => {
e.currentTarget.classList.add('aktiv');
});Die e.currentTarget-Variante ist heute weit verbreitet, weil sie unabhängig vom Funktions-Typ funktioniert und explizit ist, was gemeint ist.
Class-Felder mit Arrow — Auto-Bind-Pattern
Eine spezielle Anwendung: in einer Klasse als Field definiert, ist eine Arrow Function automatisch an die Instanz gebunden — kein bind(this) nötig.
class Counter {
wert = 0;
// Methode — this hängt vom Aufruf-Kontext ab
incMethode() { this.wert++; }
// Field mit Arrow — this ist immer die Instanz
incArrow = () => { this.wert++; };
}
const c = new Counter();
const ref1 = c.incMethode;
const ref2 = c.incArrow;
try {
ref1(); // TypeError: Cannot read properties of undefined
} catch (e) {
console.log('Methode entkoppelt:', e.message);
}
ref2();
console.log('Arrow-Field:', c.wert);Methode entkoppelt: Cannot read properties of undefined (reading 'wert')
Arrow-Field: 1Nachteil: das Arrow-Field ist pro Instanz neu allokiert — bei vielen Instanzen mehr Speicher als bei einer einzigen Methode am Prototyp. Bei Component-Frameworks (React-Klassen, Web Components) ist das Pattern trotzdem üblich, weil die Bindung-Sicherheit das Speicher-Argument aussticht.
Implicit Return — eleganter als gedacht
Ohne geschweifte Klammern liefert die Arrow Function den Wert des Ausdrucks zurück. Praktisch in Pipelines mit map/filter/reduce.
const users = [
{ name: 'Anna', alter: 30 },
{ name: 'Bob', alter: 17 },
{ name: 'Chris', alter: 25 },
];
const namen = users
.filter(u => u.alter >= 18)
.map(u => u.name)
.map(n => n.toUpperCase());
console.log(namen); // [ 'ANNA', 'CHRIS' ]
// Ohne Implicit Return wäre das deutlich umständlicher[ 'ANNA', 'CHRIS' ]Implicit Return ist genau das, was Funktional-Stil-Code lesbar macht. Eine Filter-Pipeline mit klassischen Funktionen wäre dreimal so lang.
Wann KEINE Arrow nutzen?
Eine kompakte Checkliste:
| Kontext | Arrow? | Warum |
|---|---|---|
Object-Methode mit this | nein | this ist lexikalisch, nicht das Object |
| Class-Methode am Prototyp | nein | this-Binding via Aufruf nötig |
Event-Handler, der this=Element will | nein | this ist lexikalisch |
Constructor (new MyClass()) | nein | Arrow ist nicht konstruierbar |
Funktion, die arguments braucht | nein | hat kein eigenes arguments |
Generator (function*) | nein | Arrow kennt kein yield |
| Inline-Callback (map, filter, then) | ja | kompakt, kein this-Problem |
| Top-Level kurze Helper | ja | kompakt, eindeutig |
| Class-Field für auto-bound Handler | ja | this = Instanz, kein bind nötig |
Besonderheiten
Lexical this — der Hauptgrund für Arrows
Arrow Functions binden this NICHT. Sie übernehmen das this ihres umgebenden Scopes. Das macht sie zur idealen Callback-Form in Methoden — vorher musste man bind(this) oder const self = this nutzen, um den this-Bezug zu retten.
Object-Literal mit nacktem => braucht extra Klammern
x => { name: x } ist nicht „Objekt mit Eigenschaft name", sondern ein Block mit Label name. Rückgabe: undefined. Lösung: x => ({ name: x }) — die äußeren Klammern machen es eindeutig zur Expression.
Kein arguments — aber ...args klappt
Arrows haben kein arguments-Objekt. In Practice kein Verlust, weil ...args als Rest-Parameter ein echtes Array liefert und in jedem Funktionstyp funktioniert. Rest-Parameter sind die moderne Form für variadic Funktionen.
Arrow als Class-Field — Auto-Bind ohne bind
class C { handler = () => this.foo; } bindet handler an die Instanz. Praktisch in React-Class-Components oder Web Components. Nachteil: pro Instanz allokiert, nicht am Prototyp geteilt — bei vielen Instanzen mehr Speicher.
Kein new — Arrow ist NICHT konstruierbar
new arrow() wirft TypeError: arrow is not a constructor. Arrows haben auch keinen prototype-Property. Für Constructor-Pattern: klassische Funktion oder class.
Generators als Arrow nicht möglich
function* hat keine Arrow-Variante. Generators brauchen yield, und das ist syntaktisch nur in function*-Form erlaubt. Wer einen Generator schreiben will, muss klassisch deklarieren.
Async Arrow ist möglich: async () => { await ... }
Im Gegensatz zu Generators gibt es Async Arrow Functions: const f = async () => { await ... }. Verhalten identisch zu async function() bis auf die üblichen Arrow-Unterschiede (this, arguments, new). Sehr verbreitet in modernen Codebasen.
Arrow-Funktion ohne Namen — heuristischer .name
const f = () => ... bekommt f.name === 'f' via ES2015-Heuristik aus der Zuweisung. Bei Higher-Order-Konstruktion oder anonymem Inline-Pass ist .name oft ''. Für reliable Naming gibt es bei Arrows keine „Named Expression"-Variante.
Weiterführende Ressourcen
Externe Quellen
- Arrow function expressions – MDN
- this – MDN
- Rest parameters – MDN
- Arrow Function Definitions – ECMAScript Spec