Pfeilfunktionen
Try it
Der Ausdruck einer Pfeilfunktion hat eine kürzere Syntax als ein Funktionsausdruck und hat kein eigenes this
, arguments, super, oder new.target. Solche Funktionsausdrücke sind am besten für Funktionen, die nicht als Methode genutzt werden, geeignet und können nicht als Konstruktoren verwendet werden.
Syntax
Basis Syntax
(param1, param2, …, paramN) => { statements } (param1, param2, …, paramN) => expression // gleich zu: => { return expression; } // Klammern sind optional, wenn nur ein Parametername vorhanden ist: (singleParam) => { statements } singleParam => { statements } // Die Parameterliste für eine parameterlose Funktion muss mit einem Klammernpaar geschrieben werden () => { statements }
Fortgeschrittene Syntax
// Der Body kann eingeklammert werden, um ein Objektliteral Ausdruck zurück zu geben: params => ({foo: bar}) // Rest Parameter und Default Parameter werden unterstützt (param1, param2, ...rest) => { statements } (param1 = defaultValue1, param2, …, paramN = defaultValueN) => { statements } // Destrukturierung in der Parameterliste ist ebenfalls unterstützt var f = ([a, b] = [1, 2], {x: c} = {x: a + b}) => a + b + c; f(); // 6
Beschreibung
Siehe auch "ES6 In Depth: Arrow functions" on hacks.mozilla.org.
Zwei Faktoren haben die Einführung von Pfeilfunktionen beeinflusst: kürzere Funktionen und dass this
nicht gebunden ist.
Kürzere Funktionen
var elements = [
"Hydrogen",
"Helium",
"Lithium",
"Beryllium"
];
elements.map(function(element) {
return element.length
}); // [8, 6, 7, 9]
elements.map(element => {
return element.length
}); // [8, 6, 7, 9]
elements.map(({length}) => length); // [8, 6, 7, 9]
Keine Bindung von this
Vor (der Einführung von) Pfeilfunktionen definierte jede Funktion ihren eigenen this
-Wert (d.h. ein neues Objekt im Falle eines Konstruktors; in strict mode Funktionsaufrufen nicht definiert; bzw. das kontextuelle Objekt, wenn die Funktion als eine "Objekt-Methode" aufgerufen wurde, usw.). Dies stellte sich innerhalb eines objektorientierten Programmierstils als lästig heraus.
function Person() {
// Der Person() Konstruktor definiert `this` als Instanz von sich selbst.
this.age = 0;
setInterval(function growUp() {
// Im nicht Strict Modus, definiert die growUp() Funktion `this`
// als das globale Objekt (weil das der Ort ist, an dem growUp() ausgeführt wird),
// das sich von dem `this`, welches vom Person() Konstruktor definiert wurde unterscheidet.
this.age++;
}, 1000);
}
var p = new Person();
In ECMAScript 3/5 konnte dies durch Zuweisung des Wertes von this
an eine Variable, welche umschlossen werden konnte, behoben werden.
function Person() {
var that = this;
that.age = 0;
setInterval(function growUp() {
// Der Rückruf bezieht sich auf jene `that`-Variable,
// deren Wert das zu erwartende Objekt ist.
that.age++;
}, 1000);
}
Alternativ könnte eine gebundene Funktion erstellt werden, sodass der passende this
-Wert an die growUp()
-Funktion übergeben würde.
Eine Pfeilfunktion erstellt keinen eigenen this
Kontext, wodurch this
die ursprüngliche Bedeutung des umschließenden Kontextes trägt. Somit funktioniert der folgende Code wie erwartet.
function Person(){
this.age = 0;
setInterval(() => {
this.age++; // |this| bezieht sich entsprechend auf das Person-Objekt
}, 1000);
}
var p = new Person();
Zusammenhang mit dem Strict Mode
Vorausgesetzt, dass this
aus dem umschließenden lexikalischen Kontext kommt, werden Strict Mode Regeln bezüglich this
einfach ignoriert.
var f = () => {'use strict'; return this};
f() === window; // oder das globale Objekt
Die restlichen strict mode Regeln verhalten sich normal.
Aufruf durch call oder apply
Da this
in Pfeilfunktionen nicht gebunden ist, können call()
oder apply()
Methoden nur Argumente übergeben; this
wird ignoriert:
var adder = {
base : 1,
add : function(a) {
var f = v => v + this.base;
return f(a);
},
addThruCall: function(a) {
var f = v => v + this.base;
var b = {
base : 2
};
return f.call(b, a);
}
};
console.log(adder.add(1)); // Dies würde 2 ausgeben
console.log(adder.addThruCall(1)); // Dies würde nach wie vor 2 ausgeben
Keine Bindung von Argumenten
Pfeilfunktionen haben kein eigenes arguments
Objekt. Somit ist arguments
einfach eine Referenz auf den Namen innerhalb des umschließenden Geltungsbereichs (scope).
var arguments = [1, 2, 3];
var arr = () => arguments[0];
arr(); // 1
function foo(n) {
var f = () => arguments[0] + n; // implizite Argumenten-Bindung von foo
return f();
}
foo(1); // 2
In den meisten Fällen sind Rest Parameters eine gute Alternative zum Einsatz des arguments
Objektes:
function foo(n) {
var f = (...args) => args[0] + n;
return f(10);
}
foo(1); // 11
Pfeilfunktionen als Methoden
Wie angegeben, sind Ausdrücke von Pfeilfunktionen am besten geeignet für nicht-methodische Funktionen. Man sehe, was geschieht, wenn versucht wird, sie als Methoden zu verwenden.
'use strict';
var obj = {
i: 10,
b: () => console.log(this.i, this),
c: function() {
console.log( this.i, this)
}
}
obj.b(); // gibt undefined, Window {...} aus (oder das globale Objekt)
obj.c(); // gibt 10, Object {...} aus
Pfeilfunktionen definieren (binden sozusagen) kein eigenes this
. Ein anderes Beispiel, das Object.defineProperty()
(en-US) betrifft:
'use strict';
var obj = {
a: 10
};
Object.defineProperty(obj, "b", {
get: () => {
console.log(this.a, typeof this.a, this);
return this.a+10; // stellt das globale Objekt 'Window' dar, 'this.a' gibt daher 'undefined' zurück
}
});
Verwendung des new
Operators
Pfeilfunktionen können nicht als Konstruktoren verwendet werden. Sie führen zu einem Fehler, wenn auf ihnen ein new
angewendet wird.
var Foo = () => {};
var foo = new Foo(); // TypeError: Foo is not a constructor
Einsatz der prototype
Eigenschaft
Pfeilfunktionen haben keine prototype
Eigenschaft.
var Foo = () => {};
console.log(Foo.prototype); // undefined
Verwendung des Schlüsselwortes yield
Das yield
-Schlüsselwort sollte im Körper einer Pfeilfunktion nicht verwendet werden (außer wenn dies innerhalb von darin weiter verschachtelten Funktionen erlaubt ist). Als Folge können Pfeilfunktionen nicht als Generatoren verwendet werden.
Funktionskörper
Pfeilfunktionen können entweder einen "knappen" oder einen gewöhnlichen "Blockkörper" haben.
In einem knappen Körper ist lediglich ein Ausdruck nötig und eine implizite Rückgabe wird angehängt. In einem Blockkörper muss eine explizite Rückgabe-Anweisung verwendet werden.
var func = x => x * x;
// knappe Syntax, implizierte Rückgabe
var func = (x, y) => { return x + y; };
// mit Blockkörper, explizite Rückgabe wird benötigt
Rückgabe von Objekt-Literalen
Man bedenke, dass die Rückgabe von Objekt-Literalen unter Verwendung der knappen Syntax params => {object:literal}
nicht so ausgeführt wird, wie man es erwarten würde:
var func = () => { foo: 1 };
// Der Aufruf von func() gibt undefined zurück!
var func = () => { foo: function() {} };
// SyntaxError: function-Anweisung erfordert einen Namen
Der Grund dafür ist, dass der Code in geschweiften Klammern ({}) als eine Sequenz von Anweisungen übersetzt wird (d.h. foo wird als Bezeichner behandelt und nicht als Schlüssel eines Objekt-Literals).
Man bedenke, das Objekt-Literal in Klammern zu setzen:
var func = () => ({ foo: 1 });
Zeilenumbruch
Pfeilfunktionen können keinen Zeilenumbruch zwischen Parametern und dem Pfeil haben.
var func = ()
=> 1;
// SyntaxError: Ausdruck erwartet, '=>' erhalten
Übersetzungsreihenfolge
Der Pfeil innerhalb einer Pfeilfunktion ist kein Operator. Allerdings haben Pfeilfunktionen im Vergleich zu gewöhnlichen Funktionen besondere Übersetzungsregeln, welche mit der Priorität von Operatoren (operator precedence) anders interagieren.
let callback;
callback = callback || function() {}; // ok
callback = callback || () => {};
// SyntaxError: invalid arrow-function arguments
callback = callback || (() => {}); // ok
Weitere Beispiele
// Eine leere Pfeilfunktion gibt undefined zurück
let empty = () => {};
(() => "foobar")()
// Gibt "foobar" zurück
// (Das ist eine Immediately Invoked Function Expression
// siehe IIFE im Glossar
var simple = a => a > 15 ? 15 : a;
simple(16); // 15
simple(10); // 10
let max = (a, b) => a > b ? a : b;
// Einfaches filtering, mapping, ... von Arrays
var arr = [5, 6, 13, 0, 1, 18, 23];
var sum = arr.reduce((a, b) => a + b);
// 66
var even = arr.filter(v => v % 2 == 0);
// [6, 0, 18]
var double = arr.map(v => v * 2);
// [10, 12, 26, 0, 2, 36, 46]
// Weitere knappe Zusicherungsketten (promise chains)
promise.then(a => {
// ...
}).then(b => {
// ...
});
// Parameterlose Pfeilfunktionen, welche visuell einfacher zu verstehen sind
setTimeout( _ => {
console.log("I happen sooner");
setTimeout( _ => {
// deeper code
console.log("I happen later");
}, 1);
}, 1);
Spezifikationen
Spezifikation | Status | Kommentar |
---|---|---|
ECMAScript 2015 (6th Edition, ECMA-262) Die Definition von 'Arrow Function Definitions' in dieser Spezifikation. |
Standard | Initiale Definition. |
ECMAScript (ECMA-262) Die Definition von 'Arrow Function Definitions' in dieser Spezifikation. |
Lebender Standard |
Browserkompatibilität
BCD tables only load in the browser