Funktionen sind Werte, die aufgerufen werden können. Eine Möglichkeit, eine Funktion zu definieren, nennt man Funktionsdeklaration. Der folgende Code definiert zum Beispiel die Funktion id, die einen einzelnen Parameter, x, hat:
functionid(x){returnx;}
Die return-Anweisung gibt einen Wert aus id zurück. Sie können eine Funktion aufrufen, indem Sie ihren Namen gefolgt von Argumenten in Klammern angeben:
> id('hello')
'hello'Wenn Sie nichts aus einer Funktion zurückgeben, wird (implizit) undefined zurückgegeben.
> function f() { }
> f()
undefinedDieser Abschnitt zeigte nur eine Möglichkeit, eine Funktion zu definieren und aufzurufen. Andere werden später beschrieben.
Sobald Sie eine Funktion wie gerade gezeigt definiert haben, kann sie mehrere Rollen spielen:
Sie können eine Funktion direkt aufrufen. Dann funktioniert sie als normale Funktion. Hier ist ein Beispiel für einen Aufruf:
id('hello')
Nach Konvention beginnen die Namen von normalen Funktionen mit Kleinbuchstaben.
Sie können eine Funktion über den new-Operator aufrufen. Dann wird sie zu einem Konstruktor, einer Fabrik für Objekte. Hier ist ein Beispiel für einen Aufruf:
newDate()
Nach Konvention beginnen die Namen von Konstruktoren mit Großbuchstaben.
Sie können eine Funktion in einer Eigenschaft eines Objekts speichern, was sie zu einer Methode macht, die Sie über dieses Objekt aufrufen können. Hier ist ein Beispiel für einen Aufruf:
obj.method()
Nach Konvention beginnen die Namen von Methoden mit Kleinbuchstaben.
Nicht-Methoden-Funktionen werden in diesem Kapitel erklärt; Konstruktoren und Methoden werden in Kapitel 17 erklärt.
Die Begriffe Parameter und Argument werden oft austauschbar verwendet, da der Kontext normalerweise die beabsichtigte Bedeutung klärt. Die folgende Faustregel hilft bei der Unterscheidung:
Parameter werden verwendet, um eine Funktion zu definieren. Sie werden auch als formale Parameter und formale Argumente bezeichnet. Im folgenden Beispiel sind param1 und param2 Parameter:
functionfoo(param1,param2){...}
Argumente werden verwendet, um eine Funktion aufzurufen. Sie werden auch als tatsächliche Parameter und tatsächliche Argumente bezeichnet. Im folgenden Beispiel sind 3 und 7 Argumente:
foo(3,7);
Dieser Abschnitt beschreibt drei Möglichkeiten, eine Funktion zu erstellen:
Function()Alle Funktionen sind Objekte, Instanzen von Function
functionid(x){returnx;}console.log(idinstanceofFunction);// true
Daher erhalten Funktionen ihre Methoden von Function.prototype.
Ein Funktionsausdruck erzeugt einen Wert – ein Funktions-Objekt. Zum Beispiel:
varadd=function(x,y){returnx+y};console.log(add(2,3));// 5
Der vorherige Code hat das Ergebnis eines Funktionsausdrucks der Variablen add zugewiesen und sie über diese Variable aufgerufen. Der von einem Funktionsausdruck erzeugte Wert kann einer Variablen zugewiesen (wie im letzten Beispiel gezeigt), als Argument an eine andere Funktion übergeben und mehr werden. Da normale Funktionsausdrücke keinen Namen haben, werden sie auch als anonyme Funktionsausdrücke bezeichnet.
Sie können einem Funktionsausdruck einen Namen geben.Benannte Funktionsausdrücke erlauben es einer Funktion, auf sich selbst zu verweisen, was für die Selbst-Rekursion nützlich ist:
varfac=functionme(n){if(n>0){returnn*me(n-1);}else{return1;}};console.log(fac(3));// 6
Der Name eines benannten Funktionsausdrucks ist nur innerhalb des Funktionsausdrucks zugänglich.
varrepeat=functionme(n,str){returnn>0?str+me(n-1,str):'';};console.log(repeat(3,'Yeah'));// YeahYeahYeahconsole.log(me);// ReferenceError: me is not defined
Das Folgende ist eine Funktionsdeklaration:
functionadd(x,y){returnx+y;}
Das Vorherige sieht wie ein Funktionsausdruck aus, ist aber eine Anweisung (siehe Ausdrücke versus Anweisungen). Es ist ungefähr gleichwertig mit dem folgenden Code:
varadd=function(x,y){returnx+y;};
Mit anderen Worten, eine Funktionsdeklaration deklariert eine neue Variable, erstellt ein Funktions-Objekt und weist es der Variablen zu.
Der Konstruktor Function() evaluiert JavaScript-Code, der in Zeichenketten gespeichert ist. Zum Beispiel ist der folgende Code äquivalent zum vorherigen Beispiel:
varadd=newFunction('x','y','return x + y');
Diese Art der Funktionsdefinition ist jedoch langsam und hält Code in Zeichenketten (für Werkzeuge unzugänglich). Daher ist es viel besser, wenn möglich einen Funktionsausdruck oder eine Funktionsdeklaration zu verwenden. Codeauswertung mit new Function() erklärt Function() im Detail; es funktioniert ähnlich wie eval().
Hoisting bedeutet „nach oben im Scope verschieben“. Funktionsdeklarationen werden vollständig gehoistet, Variablendeklarationen nur teilweise.
Funktionsdeklarationen werden vollständig gehoistet. Das erlaubt es Ihnen, eine Funktion aufzurufen, bevor sie deklariert wurde.
foo();functionfoo(){// this function is hoisted...}
Der Grund, warum der vorherige Code funktioniert, ist, dass JavaScript-Engines die Deklaration von foo an den Anfang des Scopes verschieben. Sie führen den Code aus, als ob er so aussehen würde:
functionfoo(){...}foo();
var-Deklarationen werden ebenfalls gehoistet, aber nur die Deklarationen, nicht die damit verbundenen Zuweisungen. Daher führt die Verwendung einer var-Deklaration und eines Funktionsausdrucks ähnlich wie im vorherigen Beispiel zu einem Fehler:
foo();// TypeError: undefined is not a functionvarfoo=function(){...};
Nur die Variablendeklaration wird gehoistet. Die Engine führt den vorherigen Code aus als:
varfoo;foo();// TypeError: undefined is not a functionfoo=function(){...};
Die meisten JavaScript-Engines unterstützen die nicht standardmäßige Eigenschaft name für Funktions-Objekte. Funktionsdeklarationen haben sie:
> function f1() {}
> f1.name
'f1'Der Name von anonymen Funktionsausdrücken ist die leere Zeichenkette:
> var f2 = function () {};
> f2.name
''Benannte Funktionsausdrücke hingegen haben einen Namen:
> var f3 = function myName() {};
> f3.name
'myName'Der Name einer Funktion ist für das Debugging nützlich. Einige Leute geben ihren Funktionsausdrücken aus diesem Grund immer Namen.
Sollten Sie eine Funktionsdeklaration wie die folgende bevorzugen?
functionid(x){returnx;}
Oder die gleichwertige Kombination aus einer var-Deklaration plus einem Funktionsausdruck?
varid=function(x){returnx;};
Sie sind im Grunde gleich, aber Funktionsdeklarationen haben zwei Vorteile gegenüber Funktionsausdrücken:
call(), apply() und bind() sind Methoden, die alle Funktionen haben (denken Sie daran, dass Funktionen Objekte sind und daher Methoden haben). Sie können beim Aufrufen einer Methode einen Wert für this bereitstellen und sind daher hauptsächlich im objektorientierten Kontext interessant (siehe Aufruf von Funktionen bei gleichzeitiger Festlegung von this: call(), apply() und bind()). Dieser Abschnitt erklärt zwei Anwendungsfälle für Nicht-Methoden.
Diese Methode verwendet die Elemente von argArray als Argumente beim Aufruf der Funktion func; das heißt, die folgenden beiden Ausdrücke sind äquivalent:
func(arg1,arg2,arg3)func.apply(null,[arg1,arg2,arg3])
thisValue ist der Wert, den this während der Ausführung von func hat. Er ist in einem nicht-objektorientierten Setting nicht notwendig und daher hier null.
apply() ist nützlich, wenn eine Funktion mehrere Argumente in einem Array-ähnlichen Format akzeptiert, aber kein Array.
Dank apply() können wir Math.max() (siehe Andere Funktionen) verwenden, um das größte Element eines Arrays zu ermitteln.
> Math.max(17, 33, 2) 33 > Math.max.apply(null, [17, 33, 2]) 33
Dies führt eine partielle Funktionsanwendung durch – eine neue Funktion wird erstellt, die func mit this auf thisValue gesetzt aufruft und die folgenden Argumente: zuerst arg1 bis argN und dann die tatsächlichen Argumente der neuen Funktion. thisValue ist im folgenden nicht-objektorientierten Setting nicht notwendig, daher ist es null.
Hier verwenden wir bind(), um eine neue Funktion plus1() zu erstellen, die wie add() ist, aber nur den Parameter y benötigt, da x immer 1 ist.
functionadd(x,y){returnx+y;}varplus1=add.bind(null,1);console.log(plus1(5));// 6
Mit anderen Worten, wir haben eine neue Funktion erstellt, die äquivalent zu folgendem Code ist:
functionplus1(y){returnadd(1,y);}
JavaScript erzwingt die Arität einer Funktion nicht: Sie können sie mit einer beliebigen Anzahl von tatsächlichen Parametern aufrufen, unabhängig davon, welche formalen Parameter definiert wurden. Daher kann die Anzahl der tatsächlichen und formalen Parameter auf zwei Arten unterschiedlich sein:
arguments (wird gleich besprochen) abgerufen werden.undefined.Die spezielle Variable arguments existiert nur innerhalb von Funktionen (einschließlich Methoden). Sie ist ein Array-ähnliches Objekt, das alle tatsächlichen Parameter des aktuellen Funktionsaufrufs enthält. Der folgende Code verwendet sie:
functionlogArgs(){for(vari=0;i<arguments.length;i++){console.log(i+'. '+arguments[i]);}}
Und hier ist die Interaktion
> logArgs('hello', 'world')
0. hello
1. worldarguments hat die folgenden Eigenschaften:
Es ist Array-ähnlich, aber kein Array. Einerseits hat es eine length-Eigenschaft, und einzelne Parameter können per Index gelesen und geschrieben werden.
Andererseits ist arguments kein Array, es ist nur ähnlich. Es hat keine der Array-Methoden (slice(), forEach() usw.). Glücklicherweise können Sie Array-Methoden ausleihen oder arguments in ein Array umwandeln, wie in Array-ähnliche Objekte und generische Methoden erklärt.
Es ist ein Objekt, daher sind alle Objektmethoden und -operatoren verfügbar. Sie können zum Beispiel den in-Operator (Iteration und Erkennung von Eigenschaften) verwenden, um zu überprüfen, ob arguments einen bestimmten Index „hat“.
> function f() { return 1 in arguments }
> f('a')
false
> f('a', 'b')
trueSie können hasOwnProperty() (Iteration und Erkennung von Eigenschaften) auf ähnliche Weise verwenden:
> function g() { return arguments.hasOwnProperty(1) }
> g('a', 'b')
trueStrict Mode verwirft mehrere der ungewöhnlicheren Funktionen von arguments:
arguments.callee bezieht sich auf die aktuelle Funktion. Es wird hauptsächlich für die Selbst-Rekursion in anonymen Funktionen verwendet und ist im Strict Mode nicht erlaubt. Als Workaround können Sie einen benannten Funktionsausdruck verwenden (siehe Benannte Funktionsausdrücke), der sich über seinen Namen selbst referenzieren kann.Im Nicht-Strict-Mode bleibt arguments auf dem neuesten Stand, wenn Sie einen Parameter ändern.
functionsloppyFunc(param){param='changed';returnarguments[0];}console.log(sloppyFunc('value'));// changed
Aber diese Art von Aktualisierung wird im Strict Mode nicht durchgeführt.
functionstrictFunc(param){'use strict';param='changed';returnarguments[0];}console.log(strictFunc('value'));// value
arguments (z. B. über arguments++). Zuweisungen an Elemente und Eigenschaften sind weiterhin erlaubt.Es gibt drei Möglichkeiten, um herauszufinden, ob ein Parameter fehlt. Erstens können Sie prüfen, ob er undefined ist:
functionfoo(mandatory,optional){if(mandatory===undefined){thrownewError('Missing parameter: mandatory');}}
Zweitens können Sie den Parameter als Boolean interpretieren. Dann wird undefined als false betrachtet. Es gibt jedoch einen Vorbehalt: Mehrere andere Werte werden ebenfalls als false betrachtet (siehe Truthy und Falsy Werte), so dass die Prüfung nicht zwischen z. B. 0 und einem fehlenden Parameter unterscheiden kann.
if(!mandatory){thrownewError('Missing parameter: mandatory');}
Drittens können Sie auch die Länge von arguments überprüfen, um eine minimale Arität zu erzwingen:
if(arguments.length<1){thrownewError('You need to provide at least 1 argument');}
Der letzte Ansatz unterscheidet sich von den anderen.
foo() und foo(undefined). In beiden Fällen wird eine Ausnahme ausgelöst.foo() aus und setzt optional auf undefined für foo(undefined).Wenn ein Parameter optional ist, bedeutet dies, dass ihm ein Standardwert zugewiesen wird, wenn er fehlt. Ähnlich wie bei Pflichtparametern gibt es vier Alternativen.
Erstens, prüfen auf undefined
functionbar(arg1,arg2,optional){if(optional===undefined){optional='default value';}}
Zweitens, interpretieren Sie optional als booleschen Wert.
if(!optional){optional='default value';}
Drittens, Sie können den Oder-Operator || verwenden (siehe Logisches Oder (||)), der den linken Operanden zurückgibt, wenn er nicht falsy ist. Andernfalls gibt er den rechten Operanden zurück.
// Or operator: use left operand if it isn't falsyoptional=optional||'default value';
Viertens, Sie können die Arität einer Funktion über arguments.length überprüfen.
if(arguments.length<3){optional='default value';}
Auch hier unterscheidet sich der letzte Ansatz von den anderen.
bar(1, 2) und bar(1, 2, undefined). In beiden Fällen ist optional 'default value'.optional auf 'default value' für bar(1, 2) und lässt es undefined (d. h. unverändert) für bar(1, 2, undefined).Eine weitere Möglichkeit ist, optionale Parameter als benannte Parameter zu übergeben, wie Eigenschaften eines Objektliterals (siehe Benannte Parameter).
In JavaScript können Sie Parameter nicht per Referenz übergeben; das heißt, wenn Sie eine Variable an eine Funktion übergeben, wird ihr Wert kopiert und an die Funktion übergeben (Wertübergabe). Daher kann die Funktion die Variable nicht ändern. Wenn Sie dies tun müssen, müssen Sie den Wert der Variablen verpacken (z. B. in einem Array).
Dieses Beispiel demonstriert eine Funktion, die eine Variable inkrementiert.
functionincRef(numberRef){numberRef[0]++;}varn=[7];incRef(n);console.log(n[0]);// 8
Wenn Sie einer anderen Funktion f eine Funktion c als Parameter übergeben, müssen Sie sich zweier Signaturen bewusst sein:
f für seinen Parameter erwartet. f kann mehrere Parameter bereitstellen, und c kann entscheiden, wie viele (falls überhaupt) davon verwendet werden.c. Zum Beispiel kann sie optionale Parameter unterstützen.Wenn sich die beiden unterscheiden, können unerwartete Ergebnisse auftreten: c könnte optionale Parameter haben, von denen Sie nichts wissen und die zusätzliche von f bereitgestellte Argumente falsch interpretieren würden.
Betrachten Sie als Beispiel die Array-Methode map() (siehe Transformationsmethoden), deren Parameter normalerweise eine Funktion mit einem einzigen Parameter ist.
> [ 1, 2, 3 ].map(function (x) { return x * x })
[ 1, 4, 9 ]Eine Funktion, die als Argument übergeben werden könnte, ist parseInt() (siehe Ganze Zahlen über parseInt()).
> parseInt('1024')
1024Sie könnten (fälschlicherweise) denken, dass map() nur ein einziges Argument bereitstellt und dass parseInt() nur ein einziges Argument akzeptiert. Dann wären Sie von folgendem Ergebnis überrascht:
> [ '1', '2', '3' ].map(parseInt) [ 1, NaN, NaN ]
map() erwartet eine Funktion mit folgender Signatur:
function(element,index,array)
Aber parseInt() hat folgende Signatur:
parseInt(string,radix?)
Somit füllt map() nicht nur string (über element), sondern auch radix (über index). Das bedeutet, dass die Werte des vorherigen Arrays wie folgt erzeugt werden:
> parseInt('1', 0)
1
> parseInt('2', 1)
NaN
> parseInt('3', 2)
NaNZusammenfassend lässt sich sagen, dass man vorsichtig mit Funktionen und Methoden sein sollte, deren Signatur man sich nicht sicher ist. Wenn Sie sie verwenden, ist es oft sinnvoll, explizit zu machen, welche Parameter empfangen und welche weitergegeben werden. Das wird über einen Callback erreicht.
> ['1', '2', '3'].map(function (x) { return parseInt(x, 10) })
[ 1, 2, 3 ]Beim Aufruf einer Funktion (oder Methode) in einer Programmiersprache muss man die tatsächlichen Parameter (vom Aufrufer angegeben) den formalen Parametern (einer Funktionsdefinition) zuordnen. Es gibt zwei gängige Möglichkeiten, dies zu tun:
Benannte Parameter haben zwei Hauptvorteile: Sie liefern Beschreibungen für Argumente in Funktionsaufrufen und sie funktionieren gut für optionale Parameter. Ich werde zuerst die Vorteile erläutern und dann zeigen, wie man benannte Parameter in JavaScript über Objektliterale simuliert.
Sobald eine Funktion mehr als einen Parameter hat, kann man verwirrt sein, wofür jeder Parameter verwendet wird. Nehmen wir zum Beispiel eine Funktion namens selectEntries(), die Einträge aus einer Datenbank zurückgibt. Angesichts des folgenden Funktionsaufrufs:
selectEntries(3,20,2);
was bedeuten diese drei Zahlen? Python unterstützt benannte Parameter, und sie machen es einfach, zu verstehen, was vor sich geht.
selectEntries(start=3,end=20,step=2)# Python syntax
Optionale Positions-Parameter funktionieren nur gut, wenn sie am Ende weggelassen werden. Irgendwo anders muss man Platzhalter wie null einfügen, damit die restlichen Parameter die richtigen Positionen haben. Bei optionalen benannten Parametern ist das kein Problem. Sie können jeden davon problemlos weglassen. Hier sind einige Beispiele:
# Python syntaxselectEntries(step=2)selectEntries(end=20,start=3)selectEntries()
JavaScript hat keine native Unterstützung für benannte Parameter wie Python und viele andere Sprachen. Aber es gibt eine recht elegante Simulation: Benenne Parameter über ein Objektliteral, das als einzelnes tatsächliches Argument übergeben wird. Wenn Sie diese Technik verwenden, sieht ein Aufruf von selectEntries() so aus:
selectEntries({start:3,end:20,step:2});
Die Funktion empfängt ein Objekt mit den Eigenschaften start, end und step. Sie können jede davon weglassen.
selectEntries({step:2});selectEntries({end:20,start:3});selectEntries();
Sie könnten selectEntries() wie folgt implementieren:
functionselectEntries(options){options=options||{};varstart=options.start||0;varend=options.end||getDbLength();varstep=options.step||1;...}
Sie können auch positionsbasierte Parameter mit benannten Parametern kombinieren. Üblich ist, dass letztere zuletzt kommen:
someFunc(posArg1,posArg2,{namedArg1:7,namedArg2:true});