Hier ist der Überblick über die Standardformulare, mit denen Funktionen erstellt werden: (Ursprünglich für eine andere Frage geschrieben, aber angepasst, nachdem sie in die kanonische Frage verschoben wurden.)
Bedingungen:
Die Schnellliste:
Funktionserklärung
"Anonymer" function
Ausdruck (der trotz des Begriffs manchmal Funktionen mit Namen erstellt)
Benannter function
Ausdruck
Accessor Function Initializer (ES5 +)
Pfeilfunktionsausdruck (ES2015 +) (der wie anonyme Funktionsausdrücke keinen expliziten Namen enthält und dennoch Funktionen mit Namen erstellen kann)
Methodendeklaration im Objektinitialisierer (ES2015 +)
Konstruktor- und Methodendeklarationen in class
(ES2015 +)
Funktionserklärung
Die erste Form ist eine Funktionsdeklaration , die folgendermaßen aussieht:
function x() {
console.log('x');
}
Eine Funktionsdeklaration ist eine Deklaration . Es ist keine Aussage oder Ausdruck. Als solches folgen Sie ihm nicht mit einem;
(obwohl dies harmlos ist).
Eine Funktionsdeklaration wird verarbeitet, wenn die Ausführung in den Kontext eintritt, in dem sie angezeigt wird, bevor ein schrittweiser Code ausgeführt wird. Die von ihr erstellte Funktion erhält einen Eigennamen (x
im obigen Beispiel), und dieser Name wird in den Bereich eingefügt, in dem die Deklaration angezeigt wird.
Da es vor jedem Schritt-für-Schritt-Code im selben Kontext verarbeitet wird, können Sie Folgendes tun:
x(); // Works even though it's above the declaration
function x() {
console.log('x');
}
Bis ES2015, decken die Spezifikation nicht , was ein JavaScript - Engine tun sollten , wenn Sie eine Funktionsdeklaration innerhalb einer Kontrollstruktur wie setzen try
, if
, switch
, while
usw., wie folgt aus :
if (someCondition) {
function foo() { // <===== HERE THERE
} // <===== BE DRAGONS
}
Und da sie verarbeitet werden, bevor Schritt-für-Schritt-Code ausgeführt wird, ist es schwierig zu wissen, was zu tun ist, wenn sie sich in einer Kontrollstruktur befinden.
Obwohl dies nicht zu tun wurde angegeben , bis ES2015, war es eine zulässige Verlängerung auf Unterstützung Funktionsdeklarationen in Blöcken. Leider (und unvermeidlich) haben verschiedene Motoren unterschiedliche Dinge getan.
Ab ES2015 steht in der Spezifikation, was zu tun ist. In der Tat gibt es drei verschiedene Dinge zu tun:
- Wenn im Loose-Modus kein Webbrowser verwendet wird, soll die JavaScript-Engine eines tun
- Im losen Modus eines Webbrowsers soll die JavaScript-Engine etwas anderes tun
- Im strengen Modus (Browser oder nicht) soll die JavaScript-Engine noch etwas anderes tun
Die Regeln für die losen Modi sind schwierig, aber im strengen Modus sind Funktionsdeklarationen in Blöcken einfach: Sie sind lokal für den Block (sie haben einen Blockumfang , der auch in ES2015 neu ist) und werden nach oben gehisst des Blocks. Damit:
"use strict";
if (someCondition) {
foo(); // Works just fine
function foo() {
}
}
console.log(typeof foo); // "undefined" (`foo` is not in scope here
// because it's not in the same block)
"Anonymer" function
Ausdruck
Die zweite gebräuchliche Form heißt anonymer Funktionsausdruck :
var y = function () {
console.log('y');
};
Wie alle Ausdrücke wird es ausgewertet, wenn es bei der schrittweisen Ausführung des Codes erreicht wird.
In ES5 hat die dadurch erstellte Funktion keinen Namen (sie ist anonym). In ES2015 wird der Funktion nach Möglichkeit ein Name zugewiesen, indem sie aus dem Kontext abgeleitet wird. Im obigen Beispiel wäre der Name y
. Ähnliches geschieht, wenn die Funktion der Wert eines Eigenschaftsinitialisierers ist. (Einzelheiten dazu und in den Regeln finden Sie SetFunctionName
in der Spezifikation. Es wird überall angezeigt .)
Benannter function
Ausdruck
Die dritte Form ist ein benannter Funktionsausdruck ("NFE"):
var z = function w() {
console.log('zw')
};
Die dadurch erstellte Funktion hat einen Eigennamen ( w
in diesem Fall). Wie alle Ausdrücke wird dies ausgewertet, wenn es bei der schrittweisen Ausführung des Codes erreicht wird. Der Name der Funktion wird nicht zu dem Bereich hinzugefügt, in dem der Ausdruck angezeigt wird. Der Name befindet sich im Gültigkeitsbereich der Funktion selbst:
var z = function w() {
console.log(typeof w); // "function"
};
console.log(typeof w); // "undefined"
Beachten Sie, dass NFEs häufig Fehlerquellen für JavaScript-Implementierungen waren. IE8 und frühere Versionen behandeln NFEs beispielsweise völlig falsch und erstellen zwei verschiedene Funktionen zu zwei verschiedenen Zeiten. Frühere Versionen von Safari hatten ebenfalls Probleme. Die gute Nachricht ist, dass aktuelle Versionen von Browsern (IE9 und höher, aktuelle Safari) diese Probleme nicht mehr haben. (Zum jetzigen Zeitpunkt ist IE8 leider weiterhin weit verbreitet, weshalb die Verwendung von NFEs mit Code für das Web im Allgemeinen immer noch problematisch ist.)
Accessor Function Initializer (ES5 +)
Manchmal können sich Funktionen weitgehend unbemerkt einschleichen. Dies ist bei Accessor-Funktionen der Fall . Hier ist ein Beispiel:
var obj = {
value: 0,
get f() {
return this.value;
},
set f(v) {
this.value = v;
}
};
console.log(obj.f); // 0
console.log(typeof obj.f); // "number"
Beachten Sie, dass ich die Funktion nicht verwendet habe ()
! Das liegt daran, dass es sich um eine Accessor-Funktion für eine Eigenschaft handelt. Wir erhalten und setzen die Eigenschaft auf normale Weise, aber hinter den Kulissen wird die Funktion aufgerufen.
Sie können Accessor-Funktionen auch mit Object.defineProperty
, Object.defineProperties
und dem weniger bekannten zweiten Argument für erstellen Object.create
.
Pfeilfunktionsausdruck (ES2015 +)
ES2015 bringt uns die Pfeil - Funktion . Hier ist ein Beispiel:
var a = [1, 2, 3];
var b = a.map(n => n * 2);
console.log(b.join(", ")); // 2, 4, 6
Sehen Sie das n => n * 2
Ding im map()
Anruf versteckt ? Das ist eine Funktion.
Ein paar Dinge über Pfeilfunktionen:
Sie haben keine eigenen this
. Stattdessen sie dicht über die this
von dem Kontext , in dem sie definiert sind. (Sie schließen auch arguments
und, falls relevant,. super
) Dies bedeutet, dass das this
in ihnen dasselbe ist wie dasthis
in dem sie erstellt wurden, und nicht geändert werden kann.
Wie Sie oben bemerkt haben, verwenden Sie das Schlüsselwort nicht function
. stattdessen verwenden Sie =>
.
Das n => n * 2
obige Beispiel ist eine Form davon. Wenn Sie mehrere Argumente haben, um die Funktion zu übergeben, verwenden Sie parens:
var a = [1, 2, 3];
var b = a.map((n, i) => n * i);
console.log(b.join(", ")); // 0, 2, 6
(Erinnere dich daran Array#map
der Eintrag als erstes Argument und der Index als zweites übergeben werden.)
In beiden Fällen ist der Funktionskörper nur ein Ausdruck; Der Rückgabewert der Funktion ist automatisch das Ergebnis dieses Ausdrucks (Sie verwenden keinen expliziten Ausdruck return
).
Wenn Sie mehr als nur einen einzelnen Ausdruck ausführen, verwenden Sie wie gewohnt {}
einen expliziten Ausdruck return
(wenn Sie einen Wert zurückgeben müssen):
var a = [
{first: "Joe", last: "Bloggs"},
{first: "Albert", last: "Bloggs"},
{first: "Mary", last: "Albright"}
];
a = a.sort((a, b) => {
var rv = a.last.localeCompare(b.last);
if (rv === 0) {
rv = a.first.localeCompare(b.first);
}
return rv;
});
console.log(JSON.stringify(a));
Die Version ohne { ... }
wird als Pfeilfunktion mit einem Ausdruckskörper oder einem prägnanten Körper bezeichnet . (Auch: Eine prägnante Pfeilfunktion.) Diejenige mit der { ... }
Definition des Körpers ist eine Pfeilfunktion mit einem Funktionskörper . (Auch: Eine ausführliche Pfeilfunktion.)
Methodendeklaration im Objektinitialisierer (ES2015 +)
ES2015 ermöglicht eine kürzere Form der Deklaration einer Eigenschaft, die auf eine Funktion verweist, die als Methodendefinition bezeichnet wird . es sieht aus wie das:
var o = {
foo() {
}
};
Das fast Äquivalent in ES5 und früher wäre:
var o = {
foo: function foo() {
}
};
Der Unterschied (außer der Ausführlichkeit) besteht darin, dass eine Methode verwendet werden kann super
, eine Funktion jedoch nicht. Wenn Sie beispielsweise ein Objekt hätten, das valueOf
mithilfe der Methodensyntax definiert (z. B.) , könnte es verwendet werden super.valueOf()
, um den Wert Object.prototype.valueOf
abzurufen (bevor Sie vermutlich etwas anderes damit tun), während die ES5-Version dies Object.prototype.valueOf.call(this)
stattdessen tun müsste .
Das bedeutet auch , dass das Verfahren einen Verweis auf das Objekt hat es auf definiert wurde, also wenn das Objekt nur vorübergehend ist (zum Beispiel, ich bin es in vorbei Object.assign
als einer der Quellobjekt), Methode Syntax könnte bedeuten , dass das Objekt gehalten wird , im Speicher, wenn es sonst zu einer Müllabfuhr hätte kommen können (wenn die JavaScript-Engine diese Situation nicht erkennt und behandelt, wenn keine der Methoden verwendet super
).
Konstruktor- und Methodendeklarationen in class
(ES2015 +)
ES2015 bringt uns class
Syntax, einschließlich deklarierter Konstruktoren und Methoden:
class Person {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
getFullName() {
return this.firstName + " " + this.lastName;
}
}
Oben sind zwei Funktionsdeklarationen aufgeführt: Eine für den Konstruktor, der den Namen erhält Person
, und eine für getFullName
die Funktion, der eine Funktion zugewiesen ist Person.prototype
.