Heute veröffentlichen wir den vierten Teil der Übersetzung des JavaScript-Handbuchs, das den Funktionen gewidmet ist.
→
Teil 1: Erstes Programm, Sprachfunktionen, Standards→
Teil 2: Codestil und Programmstruktur→
Teil 3: Variablen, Datentypen, Ausdrücke, Objekte→
Teil 4: Funktionen→
Teil 5: Arrays und Loops→
Teil 6: Ausnahmen, Semikolons, Platzhalterliterale→
Teil 7: Strict Mode, dieses Schlüsselwort, Ereignisse, Module, mathematische Berechnungen→
Teil 8: Übersicht über die ES6-Funktionen→
Teil 9: Übersicht über die ES7-, ES8- und ES9-Standards
JavaScript-Funktionen
Lassen Sie uns über Funktionen in JavaScript sprechen, sie allgemein überprüfen und die Details zu ihnen betrachten, deren Kenntnis es Ihnen ermöglicht, sie effektiv zu nutzen.
Eine Funktion ist ein unabhängiger Codeblock, der nach seiner Deklaration beliebig oft aufgerufen werden kann. Eine Funktion kann Parameter akzeptieren, obwohl dies nicht erforderlich ist. Funktionen geben einen einzelnen Wert zurück.
Funktionen in JavaScript sind Objekte bzw. Objekte vom Typ
Function
. Ihr Hauptunterschied zu gewöhnlichen Objekten, der ihnen die außergewöhnlichen Fähigkeiten verleiht, die sie besitzen, besteht darin, dass Funktionen aufgerufen werden können.
Darüber hinaus werden Funktionen in JavaScript als „erstklassige Funktionen“ bezeichnet, da sie Variablen zugewiesen, als Argumente an andere Funktionen übergeben und von anderen Funktionen zurückgegeben werden können.
Zunächst betrachten wir die Merkmale der Arbeit mit Funktionen und die entsprechenden syntaktischen Konstruktionen, die in der Sprache vor dem Aufkommen des ES6-Standards existierten und immer noch relevant sind.
So sieht eine Funktionsdeklaration aus.
function doSomething(foo) {
Heutzutage werden diese Funktionen als "normal" bezeichnet und unterscheiden sie von den "Pfeil" -Funktionen, die in ES6 angezeigt wurden.
Sie können einer Variablen oder Konstante eine Funktion zuweisen. Eine solche Konstruktion wird als Funktionsausdruck bezeichnet.
const doSomething = function(foo) {
Möglicherweise stellen Sie fest, dass die Funktion im obigen Beispiel einer Konstanten zugewiesen ist, aber selbst keinen Namen hat. Solche Funktionen werden anonym genannt. Ähnlichen Funktionen können Namen zugewiesen werden. In diesem Fall handelt es sich um einen benannten Funktionsausdruck (benannten Funktionsausdruck).
const doSomething = function doSomFn(foo) {
Die Verwendung solcher Ausdrücke erhöht die Benutzerfreundlichkeit des Debuggens (in Fehlermeldungen, in denen die Stapelverfolgung ausgeführt wird, ist der Name der Funktion sichtbar). Der Name der Funktion in einem Funktionsausdruck kann auch benötigt werden, damit sich die Funktion selbst aufrufen kann, was für die Implementierung rekursiver Algorithmen unabdingbar ist.
Im ES6-Standard sind Pfeilfunktionen erschienen, die besonders bequem in Form von sogenannten „Inline-Funktionen“ zu verwenden sind - als Argumente, die an andere Funktionen übergeben werden (Rückrufe).
const doSomething = foo => {
Pfeilfunktionen sind zusätzlich zu der Tatsache, dass die Strukturen, mit denen sie deklariert werden, kompakter sind als die Verwendung gewöhnlicher Funktionen, sie unterscheiden sich von ihnen in einigen wichtigen Merkmalen, die wir unten diskutieren werden.
Funktionsparameter
Parameter sind Variablen, die in der Phase der Deklaration einer Funktion festgelegt werden und die an sie übergebenen Werte enthalten (diese Werte werden als Argumente bezeichnet). Funktionen in JavaScript haben möglicherweise entweder keine Parameter oder einen oder mehrere Parameter.
const doSomething = () => {
Hier sind einige Beispiele für Pfeilfunktionen.
Ab dem ES6-Standard können Funktionen sogenannte „Standardparameter“ haben.
const doSomething = (foo = 1, bar = 'hey') => {
Sie stellen Standardwerte dar, die durch die Parameter von Funktionen festgelegt werden, wenn beim Aufruf die Werte einiger Parameter nicht festgelegt werden. Zum Beispiel kann die oben gezeigte Funktion sowohl durch Übergabe aller zwei empfangenen Parameter als auch durch andere Methoden aufgerufen werden.
doSomething(3) doSomething()
In ES8 können Sie jetzt nach dem letzten Argument einer Funktion ein Komma setzen (dies wird als nachfolgendes Komma bezeichnet). Mit dieser Funktion können Sie den Code einfacher bearbeiten, wenn Sie Versionskontrollsysteme während der Programmentwicklung verwenden. Details dazu finden Sie
hier und
hier .
An Funktionen übergebene Argumente können als Arrays dargestellt werden. Um diese Argumente zu analysieren, können Sie einen Operator verwenden, der wie drei Punkte aussieht (dies ist der sogenannte "Erweiterungsoperator" oder "Spread-Operator"). So sieht es aus.
const doSomething = (foo = 1, bar = 'hey') => {
Wenn Funktionen viele Parameter annehmen müssen, kann es schwierig sein, sich die Reihenfolge ihrer Reihenfolge zu merken. In solchen Fällen werden Objekte mit Parametern und Möglichkeiten zur Destrukturierung von ES6-Objekten verwendet.
const doSomething = ({ foo = 1, bar = 'hey' }) => {
Diese Technik ermöglicht es, die Parameter in Form von Objekteigenschaften zu beschreiben und die Funktion an das Objekt zu übergeben, um der Funktion den Zugriff auf die Parameter anhand ihrer Namen zu ermöglichen, ohne zusätzliche Konstruktionen zu verwenden. Lesen Sie hier mehr über diese Technik.
Von Funktionen zurückgegebene Werte
Alle Funktionen geben einen bestimmten Wert zurück. Wenn der Rückgabebefehl nicht explizit angegeben wird, gibt die Funktion
undefined
.
const doSomething = (foo = 1, bar = 'hey') => {
Die Funktionsausführung endet entweder, nachdem der gesamte darin enthaltene Code ausgeführt wurde, oder nachdem das Schlüsselwort
return
im Code gefunden wurde. Wenn dieses Schlüsselwort in einer Funktion gefunden wird, ist seine Operation abgeschlossen und die Steuerung wird an den Ort übertragen, von dem aus die Funktion aufgerufen wurde.
Wenn Sie nach dem Schlüsselwort
return
einen bestimmten Wert angeben, kehrt dieser Wert als Ergebnis der Ausführung dieser Funktion an die Stelle des Funktionsaufrufs zurück.
const doSomething = () => { return 'test' } const result = doSomething()
Von einer Funktion kann nur ein Wert zurückgegeben werden. Um mehrere Werte zurückgeben zu können, können Sie sie entweder als Objekt mit einem Objektliteral oder als Array zurückgeben und beim Aufrufen einer Funktion das destruktive Zuweisungskonstrukt verwenden. Parameternamen werden gespeichert. Wenn Sie gleichzeitig mit einem Objekt oder einem Array arbeiten müssen, das von einer Funktion zurückgegeben wird, und zwar in Form eines Objekts oder eines Arrays, können Sie auf eine destruktive Zuweisung verzichten.
const doSomething = () => { return ['Roger', 6] } const [ name, age ] = doSomething() console.log(name, age)
Die Konstruktion
const [ name, age ] = doSomething()
kann wie folgt gelesen werden: "Deklarieren Sie die Konstanten
name
und
age
und weisen Sie ihnen die Werte der Elemente des Arrays zu, die die Funktion zurückgeben wird."
So sieht das gleiche mit einem Objekt aus.
const doSomething = () => { return {name: 'Roger', age: 6} } const { name, age } = doSomething() console.log(name, age)
Verschachtelte Funktionen
Funktionen können in anderen Funktionen deklariert werden.
const doSomething = () => { const doSomethingElse = () => {} doSomethingElse() return 'test' } doSomething()
Der Umfang einer verschachtelten Funktion wird durch eine externe Funktion begrenzt, die nicht von außen aufgerufen werden kann.
Objektmethoden
Wenn Funktionen als Eigenschaften von Objekten verwendet werden, werden solche Funktionen als Objektmethoden bezeichnet.
const car = { brand: 'Ford', model: 'Fiesta', start: function() { console.log(`Started`) } } car.start()
Dieses Schlüsselwort
Wenn wir Pfeil- und gewöhnliche Funktionen vergleichen, die als Methoden von Objekten verwendet werden, können wir ihren wichtigen Unterschied feststellen, der in der Bedeutung des Schlüsselworts
this
. Betrachten Sie ein Beispiel.
const car = { brand: 'Ford', model: 'Fiesta', start: function() { console.log(`Started ${this.brand} ${this.model}`) }, stop: () => { console.log(`Stopped ${this.brand} ${this.model}`) } } car.start()
Wie Sie sehen, führt der Aufruf der Methode
start()
zum erwarteten Ergebnis, aber die Methode
stop()
funktioniert offensichtlich nicht richtig.
Dies liegt an der Tatsache, dass sich das Schlüsselwort this bei Verwendung in Pfeil- und normalen Funktionen unterschiedlich verhält. Das
this
in der Pfeilfunktion enthält nämlich einen Link zu dem Kontext, der die Funktion enthält. In diesem Fall ist dieser Kontext beim Browser das
window
.
So sieht die Ausführung eines solchen Codes in der Browserkonsole aus.
const test = { fn: function() { console.log(this) }, arrFn: () => { console.log(this) } } test.fn() test.arrFn()
Dieses Schlüsselwort ist in herkömmlichen und Pfeilfunktionen enthaltenWie Sie sehen können, bedeutet das Aufrufen in einer regulären Funktion das Aufrufen des Objekts, und
this
in der Pfeilfunktion zeigt auf das
window
.
All dies bedeutet, dass Pfeilfunktionen nicht für die Rolle von Objekt- und Konstruktormethoden geeignet sind (wenn Sie versuchen, die
TypeError
als Konstruktor zu verwenden, wird ein
TypeError
).
Funktionsausdrücke werden sofort aufgerufen
Der sofort aufgerufene Funktionsausdruck (IIFE) ist eine Funktion, die unmittelbar nach ihrer Deklaration automatisch aufgerufen wird.
;(function () { console.log('executed') })()
Das Semikolon vor IIFE ist optional, aber seine Verwendung ermöglicht es Ihnen, sich gegen Fehler zu versichern, die mit der automatischen Platzierung von Semikolons verbunden sind.
Im obigen Beispiel wird das
executed
Wort an die Konsole gesendet, wonach IIFE beendet wird. IIFE kann wie andere Funktionen die Ergebnisse ihrer Arbeit zurückgeben.
const something = (function () { return 'IIFE' })() console.log(something)
Nach dem Ausführen dieses einfachen Beispiels erhält die Konsole die
IIFE
Zeile, die sich nach dem Ausführen des sofort aufgerufenen Funktionsausdrucks als
something
herausstellte. Es scheint, dass ein solches Design keinen besonderen Nutzen bringt. Wenn jedoch in IIFE einige komplexe Berechnungen durchgeführt werden, die nur einmal durchgeführt werden müssen, wonach die entsprechenden Mechanismen nicht mehr erforderlich sind, liegt der Nutzen von IIFE auf der Hand. Bei diesem Ansatz ist nämlich nach Ausführung von IIFE nur das von der Funktion zurückgegebene Ergebnis im Programm verfügbar. Darüber hinaus können wir uns daran erinnern, dass Funktionen andere Funktionen und Objekte zurückgeben können. Wir sprechen über Schließungen, wir werden unten darüber sprechen.
Funktionserweiterung
Bevor der JavaScript-Code ausgeführt wird, wird er neu organisiert. Wir haben bereits über den Hebemechanismus für Variablen gesprochen, die mit dem Schlüsselwort
var
deklariert wurden. Ein ähnlicher Mechanismus funktioniert mit Funktionen. Wir sprechen nämlich von der Tatsache, dass Deklarationen von Funktionen während der Verarbeitung des Codes vor seiner Ausführung in den oberen Teil ihres Gültigkeitsbereichs verschoben werden. Als Ergebnis stellt sich beispielsweise heraus, dass Sie die Funktion aufrufen können, bevor sie deklariert wird.
doSomething()
Wenn Sie einen Funktionsaufruf so verschieben, dass er seiner Deklaration folgt, ändert sich nichts.
Wenn in einer ähnlichen Situation ein Funktionsausdruck verwendet wird, gibt ein ähnlicher Code einen Fehler aus.
doSomething()
In diesem Fall stellt sich heraus, dass die Deklaration der Variablen
doSomething
an die Spitze des Bereichs steigt, dies jedoch nicht für die Zuweisungsoperation gilt.
Wenn Sie in einer ähnlichen Situation anstelle von
var
die Schlüsselwörter
let
oder
const
, funktioniert dieser Code ebenfalls nicht. Das System zeigt jedoch eine andere Fehlermeldung an (
ReferenceError
anstelle von
TypeError
), da bei Verwendung von
let
und
const
keine Variablen- und Konstantendeklarationen
TypeError
werden.
Pfeilfunktionen
Jetzt werden wir mehr über Pfeilfunktionen sprechen, die wir bereits getroffen haben. Sie können als eine der bedeutendsten Neuerungen des ES6-Standards angesehen werden. Sie unterscheiden sich von gewöhnlichen Funktionen nicht nur im Aussehen, sondern auch in ihrem Verhalten. Heutzutage sind sie extrem weit verbreitet. Vielleicht gibt es kein einziges modernes Projekt, bei dem sie in den allermeisten Fällen nicht verwendet würden. Wir können sagen, dass ihr Aussehen das Aussehen des JS-Codes und die Merkmale seiner Arbeit für immer verändert hat.
Aus rein externer Sicht ist die Syntax zum Deklarieren von Pfeilfunktionen kompakter als die Syntax gewöhnlicher Funktionen. Hier ist eine reguläre Funktionsdeklaration.
const myFunction = function () {
Hier ist die Ankündigung der Pfeilfunktion, die im Allgemeinen, wenn Sie die Merkmale der Pfeilfunktionen nicht berücksichtigen, der vorherigen ähnlich ist.
const myFunction = () => {
Wenn der Hauptteil einer Pfeilfunktion nur einen Befehl enthält, dessen Ergebnis diese Funktion zurückgibt, kann er ohne geschweifte Klammern und ohne das Schlüsselwort
return
. Eine solche Funktion gibt beispielsweise die Summe der an sie übergebenen Argumente zurück.
const myFunction = (a,b) => a + b console.log(myFunction(1,2))
Wie Sie sehen können, sind die Parameter der Pfeilfunktionen wie bei normalen Funktionen in Klammern beschrieben. Wenn eine solche Funktion nur einen Parameter akzeptiert, kann sie außerdem ohne Klammern angegeben werden. Hier ist beispielsweise eine Funktion, die das Ergebnis der Division der übergebenen Zahl durch 2 zurückgibt.
const myFunction = a => a / 2 console.log(myFunction(8))
Infolgedessen stellt sich heraus, dass Pfeilfunktionen in Situationen, in denen kleine Funktionen benötigt werden, sehr praktisch sind.
▍ Implizite Rückgabe von Funktionsergebnissen
Wir haben dieses Merkmal der Pfeilfunktionen bereits angesprochen, aber es ist so wichtig, dass es ausführlicher besprochen werden sollte. Wir sprechen über die Tatsache, dass einzeilige Pfeilfunktionen die implizite Rückgabe der Ergebnisse ihrer Arbeit unterstützen. Ein Beispiel für die Rückgabe eines primitiven Werts aus einer einzeiligen Pfeilfunktion, die wir bereits gesehen haben. Was ist, wenn eine solche Funktion ein Objekt zurückgeben sollte? In diesem Fall können die geschweiften Klammern des Objektliteral das System verwirren, sodass im Hauptteil der Funktion Klammern verwendet werden.
const myFunction = () => ({value: 'test'}) const obj = myFunction() console.log(obj.value)
▍ Schlüsselwort this und Pfeilfunktionen
Als wir uns oben die Funktionen
this
, haben wir reguläre und Pfeilfunktionen verglichen. Dieser Abschnitt soll Ihre Aufmerksamkeit auf die Bedeutung ihrer Unterschiede lenken. Das
this
kann an sich bestimmte Schwierigkeiten verursachen, da es vom Kontext der Codeausführung abhängt und davon, ob der strikte Modus aktiviert ist oder nicht.
Wie wir gesehen haben, zeigt dies bei Verwendung des
this
in einer Methode eines Objekts, das durch eine reguläre Funktion dargestellt wird, auf das Objekt, zu dem die Methode gehört. In diesem Fall geht es darum, das Schlüsselwort
this
an einen Wert zu binden, der den Kontext der Funktion darstellt. Insbesondere wenn eine Funktion als Objektmethode aufgerufen wird, ist das Schlüsselwort this an dieses Objekt gebunden.
Bei Pfeilfunktionen stellt sich heraus, dass
this
Bindung in ihnen nicht ausgeführt wird. Sie verwenden das
this
aus ihrem Gültigkeitsbereich. Daher werden sie nicht zur Verwendung als Objektmethoden empfohlen.
Das gleiche Problem tritt auf, wenn Funktionen als Ereignishandler für DOM-Elemente verwendet werden. Beispielsweise wird die HTML-Elementschaltfläche verwendet, um Schaltflächen zu beschreiben. Das
click
wird
click
wenn ein Benutzer auf eine Schaltfläche klickt. Um auf dieses Ereignis im Code zu reagieren, müssen Sie zuerst einen Link zum entsprechenden Element abrufen und ihm dann als Funktion einen
click
zuweisen. Als solcher Handler können Sie sowohl die reguläre Funktion als auch die Pfeilfunktion verwenden. Wenn Sie jedoch im Ereignishandler auf das Element zugreifen müssen, für das es aufgerufen wird (dh
this
), funktioniert die Pfeilfunktion hier nicht, da der darin verfügbare Wert auf das
window
verweist. Um dies in der Praxis zu testen, erstellen Sie eine HTML-Seite, deren Code unten angezeigt wird, und klicken Sie auf die Schaltflächen.
<!DOCTYPE html> <html> <body> <button id="fn">Function</button> <button id="arrowFn">Arrow function</button> <script> const f = document.getElementById("fn") f.addEventListener('click', function () { alert(this === f) }) const af = document.getElementById("arrowFn") af.addEventListener('click', () => { alert(this === window) }) </script> </body> </html>
In diesem Fall werden beim Klicken auf diese Schaltflächen Fenster mit
true
angezeigt. In der Klickereignishandler der Schaltfläche mit dem Bezeichner
fn
wird jedoch die Gleichheit von
this
der Schaltfläche überprüft, und in der Schaltfläche mit dem BezeichnerpfeilFn wird die Gleichheit von
this
und dem Objekt des
window
überprüft.
Wenn Sie
this
im Ereignishandler des HTML-Elements aufrufen müssen, funktioniert die Pfeilfunktion daher für das Design eines solchen Handlers nicht.
Kurzschlüsse
Verschlüsse sind ein wichtiges Konzept in JavaScript. Wenn Sie JS-Funktionen geschrieben haben, haben Sie auch Verschlüsse verwendet. Verschlüsse werden in einigen Entwurfsmustern verwendet - für den Fall, dass Sie eine strikte Kontrolle des Zugriffs auf bestimmte Daten oder Funktionen organisieren müssen.
Wenn eine Funktion aufgerufen wird, hat sie Zugriff auf alles, was sich im externen Bereich befindet. Es gibt jedoch keinen Zugriff auf das, was in der Funktion deklariert ist. Das heißt, wenn eine Variable (oder eine andere Funktion) in einer Funktion deklariert wurde, kann entweder während der Ausführung der Funktion oder nach Abschluss ihrer Arbeit nicht auf externen Code zugegriffen werden. Wenn jedoch eine andere Funktion von der Funktion zurückgegeben wird, hat diese neue Funktion Zugriff auf alles, was in der ursprünglichen Funktion deklariert wurde. In diesem Fall wird all dies vor dem externen Code im Abschluss verborgen.
Betrachten Sie ein Beispiel. Hier ist eine Funktion, die den Namen des Hundes übernimmt und ihn dann in der Konsole anzeigt.
const bark = dog => { const say = `${dog} barked!` ;(() => console.log(say))() } bark(`Roger`)
Der von dieser Funktion zurückgegebene Wert interessiert uns noch nicht, der Text wird mit IIFE in der Konsole angezeigt, was in diesem Fall keine besondere Rolle spielt. Dies hilft uns jedoch, den Zusammenhang zwischen dieser Funktion und ihrer Variante zu erkennen, in der anstelle einer angezeigten Funktion eine Funktion aufgerufen wird Text an die Konsole, wir werden diese Funktion von der umgeschriebenen Funktion
bark()
.
const prepareBark = dog => { const say = `${dog} barked!` return () => console.log(say) } const bark = prepareBark(`Roger`) bark()
Das Ergebnis des Codes ist in zwei Fällen dasselbe. Im zweiten Fall wird jedoch das, was beim Aufrufen auf die ursprüngliche Funktion übertragen wurde (der Name des Hundes,
Roger
), im Verschluss gespeichert und anschließend von einer anderen vom Original zurückgegebenen Funktion verwendet.
Lassen Sie uns ein weiteres Experiment durchführen - erstellen Sie mit der ursprünglichen Funktion zwei neue für verschiedene Hunde.
const prepareBark = dog => { const say = `${dog} barked!` return () => { console.log(say) } } const rogerBark = prepareBark(`Roger`) const sydBark = prepareBark(`Syd`) rogerBark() sydBark()
Dieser Code gibt Folgendes aus.
Roger barked! Syd barked!
Es stellt sich heraus, dass der Wert der
say
Konstante an die Funktion gebunden ist, die von der
prepareBark()
-Funktion zurückgegeben wird.
Beachten Sie,
say
beim
prepareBark()
Aufruf von
prepareBark()
ein neuer Wert
prepareBark()
wird, während sich der Wert, der beispielsweise
say
ersten Aufruf von
prepareBark()
aufgezeichnet wurde
say
nicht ändert. Der Punkt ist, dass mit jedem Aufruf dieser Funktion ein neuer Abschluss erstellt wird.
Zusammenfassung
Heute haben wir über gewöhnliche und Pfeilfunktionen gesprochen, über die Merkmale ihrer Deklaration und Verwendung, darüber, wie sich
this
Schlüsselwort in verschiedenen Situationen verhält, und über Schließungen. Das nächste Mal diskutieren wir Arrays und Schleifen.
Liebe Leser! Wie stehen Sie zu Pfeilfunktionen in JavaScript?
