Funktionen sind einer der fundamentalen Aufbaublöcke von JavaScript. Eine Funktion ist eine Prozedur - ein Satz von Anweisungen die eine Aufgabe erledigen oder einen Wert berechnen. Damit eine Funktion benutzt werden kann, muss diese im selben Sichtbarkeitsbereich definiert werden, von dem aus sie aufgerufen werden soll.
Definieren von Funktionen
Eine Funktionsdefinition (auch Funktionsdeklaration genannt) setzt sich zusammen aus dem Schlüsselwort function
, gefolgt von:
- Dem Namen der Funktion.
- Einer Liste von Argumenten, die durch ein Komma getrennt und in Klammern eingeschlossen werden.
- Den JavaScript Anweisungen, welche definieren, was die Funktion tun soll, eingeschlossen in geschweifte Klammern.
Der folgende Code definiert zum Beispiel eine einfache Funktion square
:
function square(number) {
return number * number;
}
Diese Funktion nimmt ein Argument number
entgegen. Die Funktion besteht aus einer Anweisung, die festlegt, dass number
mit sich selbst multipliziert und das Ergebnis zurückgeliefert werden soll. Mit dem Schlüsselwort return
wird also der Rückgabewert der Funktion festgelegt.
return number * number;
Einfache Parameter (wie z. B. Numbers) werden als Wert (by value) an die Funktion übergeben. Das bedeutet, der Wert wird zwar an die Funktion übergeben, jedoch gelten Änderungen des Werts, die von der Funktion vorgenommen werden, nur innerhalb der Funktion selbst und nicht etwa global oder in der aufrufenden Funktion.
Wird hingegen ein Objekt (z. B. ein nicht-primitiver Wert, wie ein Array oder ein benutzerdefiniertes Objekt) als Parameter an eine Funktion übergeben und Eigenschaften dieses Objekts von der Funktion verändert, so gelten diese Änderungen auch außerhalb der Funktion.
Zum Beispiel:
function myFunc(theObject) {
theObject.make = "Toyota";
}
var mycar = {make: "Honda", model: "Accord", year: 1998},
x,
y;
x = mycar.make; // x bekommt den Wert "Honda"
myFunc(mycar);
y = mycar.make; // y bekommt den Wert "Toyota"
// (Die make-Eigenschaft wurde von der Funktion geändert)
Es sei angemerkt, dass die Zuweisung eines neuen Objekts an den Parameter innerhalb der Funktion das übergebene Objekt nicht verändert, da hierdurch nur der Wert des Parameters selbst geändert wird:
function myFunc(theObject) {
theObject = {make: "Ford", model: "Focus", year: 2006};
}
var mycar = {make: "Honda", model: "Accord", year: 1998},
x,
y;
x = mycar.make; // x bekommt den Wert "Honda"
myFunc(mycar);
y = mycar.make; // y bekommt noch einmal den Wert "Honda"
Beim ersten Beispiel wurde das Objekt mycar
an die Funktion myFunc
übergeben,
welche das Objekt veränderte. Beim zweiten Beispiel wurde das
übergebene Objekt von der Funktion nicht verändert; stattdessen wurde
eine neue lokale Variable mit dem selben Namen erzeugt, was keinen
Effekt auf das übergebene Objekt selbst hatte.
Bei JavaScript kann eine Funktion abhängig von einer Bedingung definiert werden:
if (num == 0){
function myFunc(theObject) {
theObject.make = "Toyota"
}
}
Sollte num
nicht dem Wert 0
entsprechen, wird die Funktion nicht definiert und jeder Versuch eines Aufrufs scheitert.
Achtung: ECMAScript erlaubt die Definition von Funktionen in solchem Kontext nicht, sondern nur direkt innerhalb anderer Funktionen beim Top Level eines Skripts. Aus diesem Grund ist das Beispiel nicht korrekt für ECMAScript.
Vorsicht: Die verschiedenen Implemetierungen von JavaScript behandeln dieses Nicht-Standard-Konstrukt unterschiedlich. Beim Schreiben von portablem Code sollte also besser darauf verzichtet werden. Andernfalls kann es passieren, dass der Code nicht in allen Browsern funktioniert.
Neben den hier beschriebenen Möglichkeiten zur Definition von Funktionen
kann auch der Function
-Konstruktor eingesetzt werden,
um Funktionen aus einem String während der Laufzeit zu erzeugen,
ähnlich wie bei eval()
.
Eine Methode ist eine Funktion, die eine Eigenschaft eines Objekts ist. Genauere Informationen findet man im Kapitel Mit Objekten arbeiten.
Während die oben beschriebenen Funktionsdeklarationen alle syntaktische Anweisungen sind,
können Funktionen auch mit einem Funktionsausdruck erstellt werden.
Solch eine Funktion kann anonym sein; sie muss keinen Namen besitzen.
Zum Beispiel hätte die Funktion square
auch so definiert werden können:
var square = function(number) {return number * number};
Es kann auch ein Name für die Funktion angegeben und die Funktion selbst mit Hilfe dieses Namens innerhalb der Funktion angesprochen werden. Außerdem kann die Festlegung eines Funktionsnamens dabei helfen, in Stack Traces die Funktion identifizieren zu können:
var factorial = function fac(n) {return n<2 ? 1 : n*fac(n-1)};
print(factorial(3));
Funktionsausdrücke sind praktisch wenn eine Funktion als Argument an
eine andere Funktion übergeben werden soll. Das folgende Beispiel zeigt
eine Funktion map
, die zuerst definiert und dann aufgerufen wird, mit einer anonymen Funktion als Parameter.
function map(f,a) {
var result = [], // Neues Array
i;
for (i = 0; i != a.length; i++)
result[i] = f(a[i]);
return result;
}
Der folgende Code gibt [0, 1, 8, 125, 1000]
zurück. Für jedes Element des Arrays wird die anonyme Funktion einmal aufgerufen.
map(function(x) {return x * x * x}, [0, 1, 2, 5, 10]);
Aufrufen von Funktionen
Bei der Definition einer Funktion wird diese nicht ausgeführt,
sondern nur ein Name zugewiesen und der Code festgelegt, der beim Aufruf
der Funktion ausgeführt wird. Erst beim Aufruf einer Funktion wird der Code ausgeführt. Zum Beispiel kann die Funktion square
wie folgt aufgerufen werden:
square(5);
Die voranstehende Anweisung ruft die Funktion mit einem Argument 5 auf. Daraufhin werden die Anweisungen der Funktion ausgeführt und der Wert 25 zurückgegeben.
Funktionen müssen beim Aufruf innerhalb desselben Sichtbarkeitsbereichs liegen, jedoch kann die Deklaration auch erst unterhalb eines Funktionsaufrufs stattfinden:
print(square(5)); // Aufruf /* ... */ function square(n){return n*n} // Deklaration
Die Argumente einer Funktion sind nicht auf bestimmte Datentypen wie
Strings oder Numbers beschränkt. Es können auch ganze Objekte an eine
Funktion übergeben werden.
Die Funktion show_props
(aus dem Abschnitt
Mit Objekten arbeiten) ist ein Beispiel für eine Funktion,
die ein Objekt als Argument entgegennimmt.
Eine Funktion kann rekursiv aufgebaut sein. Das bedeutet, dass die Funktion sich selbst aufruft. Das nachfolgende Beispiel stellt eine rekursive Funktion für die Berechnung von Fakultäten dar.
function factorial(n){
if ((n == 0) || (n == 1))
return 1;
else
return (n * factorial(n - 1));
}
Mit dieser Funktion lassen sich z. B. die Fakultäten von eins bis fünf berechnen:
var a, b, c, d, e;
a = factorial(1); // a gets the value 1
b = factorial(2); // b gets the value 2
c = factorial(3); // c gets the value 6
d = factorial(4); // d gets the value 24
e = factorial(5); // e gets the value 120
Es gibt noch weitere Möglichkeiten, wie Funktionen aufgerufen werden können. Des Öfteren kommt es vor, dass eine Funktion dynamisch aufgerufen werden soll, die Anzahl der Argumente variiert oder der Kontext beim Funktionsaufruf einem bestimmten Objekt während der Laufzeit zugeordnet werden soll. Daher sollte man wissen, dass Funktionen ebenfalls Objekte sind, welche entsprechende durch die Sprache festgelegte Standardmethoden besitzen (siehe Funktions-Objekte). Mit einer dieser Methoden, der Methode apply(), kann das Ziel der Zuordnung einer Funktion zu einem Objekt erreicht werden.
Sichtbarkeit von Funktionen
Auf Variablen, die innerhalb von Funktionen definiert werden, kann nicht von außerhalb der Funktion zugegriffen werden, da die Sichtbarkeit auf den Bereich der Funktion beschränkt ist. Jedoch kann eine Funktion auf alle Variablen und Funktionen zugreifen, die innerhalb des Sichtbarkeitsbereichs definiert wurden. Mit anderen Worten: Eine Funktion, die im globalen Sichtbarkeitsbereich definiert wurde, hat Zugriff auf alle Variablen, die im globalen Sichtbarkeitsbereich definiert wurden. Eine Funktion, die innerhalb einer anderen Funktion definiert wurde, hat auch Zugriff auf alle Variablen der äußeren Funktion und Variablen auf die die äußere Funktion Zugriff hat.
// Definition im globalen Sichtbarkeitsbereich
var num1 = 20,
num2 = 3,
name = "Chamahk";
// Funktion ebenfalls im globalen Sichtbarkeitsbereich definiert
function multiply() {
return num1 * num2;
}
multiply(); // Gibt 60 zurück
// add() ist eine eingebettete Funktion (engl. nested function)
function getScore () {
var num1 = 2,
num2 = 3;
function add() {
return name + " scored " + (num1 + num2);
}
// Aufruf der Funktion und Ergebnis ist Rückgabewert
return add();
}
getScore(); // Gibt zurück: "Chamahk scored 5"
Closures
Closures sind eines der mächtigsten Features von JavaScript. JavaScript erlaubt die Einbettung von Funktionen und erlaubt der inneren Funktion vollen Zugriff auf alle Variablen und Funktionen, die innerhalb der äußeren Funktion definiert wurden (und allen anderen Variablen und Funktionen, die von der Funktion aus ansprechbar sind). Jedoch hat die äußere Funktion keinen Zugriff auf die Variablen und Funktionen der inneren Funktion. Dieser eingeschränkte Zugriff stellt einen Sicherheitsmechanismus für die Variablen der inneren Funktion dar. Falls die innere Funktion länger bestehen bleibt als die äußere Funktion, bleiben die Variablen und Funktionen der äußeren Funktion länger erhalten als die Funktion selbst, da die innere Funktion Zugang zum Sichtbarkeitsbereich der äußeren Funktion hat. Eine Closure wird erstellt, wenn die innere Funktion für den Sichtbarkeitsbereich außerhalb der äußeren Funktion zugänglich gemacht wird.
var pet = function(name) { // Die äußere Funktion definiert eine Variable "name"
var getName = function() {
return name; // Die innere Funktion hat Zugriff auf die Variable "name" der äußeren Funktion
}
return getName; // Rückgabe der inneren Funktion; damit wird sie für die äußeren Sichtbarkeitsbereiche zugänglich gemacht
},
myPet = pet("Vivie");
myPet(); // Gibt "Vivie" zurück
Der Aufbau kann sehr viel komplexer sein, als bei diesem Beispiel, z.B. kann auch ein Objekt, das Methoden für die Manipulation der inneren Variablen der äußeren Funktion besitzt, zurückgegeben werden.
var createPet = function(name) {
var sex;
return {
setName: function(newName) {
name = newName;
},
getName: function() {
return name;
},
getSex: function() {
return sex;
},
setSex: function(newSex) {
if(typeof newSex == "string" && (newSex.toLowerCase() == "male" || newSex.toLowerCase() == "female")) {
sex = newSex;
}
}
}
}
var pet = createPet("Vivie");
pet.getName(); // Vivie
pet.setName("Oliver");
pet.setSex("male");
pet.getSex(); // male
pet.getName(); // Oliver
Beim Code aus dem Beispiel ist die Variable name
der
äußere Funktion für die inneren Funktionen zugänglich und es gibt keine
andere Möglichkeit auf die inneren Variablen zuzugreifen, außer durch
die inneren Funktionen. Die inneren Variablen der inneren Funktion
arbeiten wie ein abgesicherter Speicherbereich der inneren Funktionen.
Sie halten "beständige" abgesicherte Daten, womit die inneren Funktionen
arbeiten können.
Es ist nicht zwingend erforderlich die Funktionen einer Variablen zuzuordnen oder ihnen Namen zu geben:
var getCode = (function(){
var secureCode = "0]Eal(eh&2"; // A code we do not want outsiders to be able to modify...
return function () {
return secureCode;
};
})();
getCode(); // Returns the secret code
Leider gibt es ein paar Fallstricke, die beim Einsatz von Closures vermieden werden sollten. Falls in einer eingeschlossene Funktion eine Variable mit demselben Namen wie der Name einer Variablen der äußeren Funktion definiert wird, gibt es keine Möglichkeit mehr, die Variable der äußeren Funktion anzusprechen.
var createPet = function(name) { // Outer function defines a variable called "name"
return {
setName: function(name) { // Enclosed function also defines a variable called "name"
name = name; // ??? How do we access the "name" defined by the outer function ???
}
}
}
Der Umgang mit der magischen Variablen this
kann sehr
trickreich sein bei der Verwendung in Closures und es ist ein
vorsichtiger Umgang erforderlich. Der Sichtbarkeitsbereich auf den sich
das Schlüsselwort this
bezieht, hängt beim Einsatz mit
Closures davon ab, an welcher Stelle die Funktion aufgerufen wird und
nicht wie üblich, wo sie definiert wurde. Einen exzellenten Artikel über
Closures findet man hier.
Benutzung des Argument-Objekts
Die Argumente einer Funktion werden in einem Array-artigen Objekt gespeichert und können innerhalb der Funktion wie folgt angesprochen werden:
arguments[i]
wobei i
die Nummer des Arguments ist, angefangen bei 0. Das erste Argument wird demnach mit arguments[0]
angesprochen. Die Anzahl der Argumente ist arguments.length
.
Mit Hilfe des Argument-Objekts kann eine Funktion mit mehr Argumenten
aufgerufen werden, als sie eigentlich formal entgegennimmt. Dies ist
sehr nützlich, falls man nicht im Klaren darüber ist, wievielen
Argumenten an eine Funktion übergeben werden sollen. Über arguments.length
lässt sich die Anzahl der Argumente herausfinden und diese können dann über das Objekt arguments
angesprochen werden.
Hat man z.B. eine Funktion für das Verbinden mehrerer Strings und das einzige formal definierte Argument ist das Zeichen, welches zwischen den Strings stehen soll,
function myConcat(separator) {
var result = "", // initialize list
i;
// iterate through arguments
for (i = 1; i < arguments.length; i++) {
result += arguments[i] + separator;
}
return result;
}
so kann man eine beliebige Anzahl von Argumenten an diese Funktion übergeben und die Funktion verbindet alle Strings miteinander zu einer Art Liste, die als String gespeichert wird.
// returns "red, orange, blue, "
myConcat(", ", "red", "orange", "blue");
// returns "elephant; giraffe; lion; cheetah; "
myConcat("; ", "elephant", "giraffe", "lion", "cheetah");
// returns "sage. basil. oregano. pepper. parsley. "
myConcat(". ", "sage", "basil", "oregano", "pepper", "parsley");
Vorsicht: Das Argument Objekt ist zwar Array-artig insofern dass es auch einen nummerierten Index und eine Eigenschaft length
hat, jedoch ist es kein Array, hat also nicht alle Methoden, die normalerweise Arrays besitzen.
In der Referenz findet man im Abschnitt zum Funktions
-Objekt weitere Informationen.
Vordefinierte Funktionen
- eval
- isFinite
- isNaN
- parseInt und parseFloat
- Number und String
- encodeURI, decodeURI, encodeURIComponent, und decodeURIComponent (alle ab Javascript 1.5 verfügbar).
Der folgende Abschnitt stellen diese Funktionen vor. Weitere Informationen findet man in der Javascript-Referenz.
eval-Funktion
Die eval Funktion führt JavaScript-Code aus, ohne eine Referenz zu einem Objekt.
Die Syntax von eval ist wie folgt:
eval(Ausdruck);
wobei Ausdruck ein String ist, der JavaScript-Code enthält.
Wenn der String ein Ausdruck ist, wird dieser von eval ausgewertet. Wenn das Argument einen oder mehrere JavaScript-Ausdrücke enthält, werden diese von eval ausgeführt. Der Sichtbarkeitsbereich (Scope) von eval ist identisch mit dem des aufrufeneden Codes. Achtung: eval sollte nicht zur Auswertung von arithmetischen Ausdrücken verwendet werden, da Javascript solche Ausdrücke automatisch auswertet.
isFinite-Funktion
Die isFinite-Funktion evaluiert ein Argument und stellt fest, ob es sich um eine endliche Zahl handelt.
Syntax:
isFinite(Number);
wobei Number die betreffende Zahl ist.
Falls die Zahl NaN, positiv unendlich oder negativ unenedlich ist, gibt die Funktion false zurück, andernfalls wird true zurückgegeben. Der folgende Code überprüft die Client-Eingabe und stellt fest, ob es sich um eine endliche Zahl handelt.
if(isFinite(ClientInput)){
/* take specific steps */
}
isNaN-Funktion
Die isNaN-Funktion wertet eine Zahl aus und prüft, ob sie keine Zahl ist (NaN = engl. not a number).
Syntax:
isNaN(testWert);
wobei testWert der Wert ist, der überprüft wird. Die Funktionen parseFloat und parseInt geben "NaN" zurück, wenn sie feststellen, dass es sich nicht um eine Zahl handelt. Die Funktion isNaN gibt true zurück, wenn der Wert keine Zahl ist (NaN) und andernfalls false.
Beim folgenden Code wird floatValue ausgewertet, um festzustellen, ob es sich um eine Zahl handelt und anschließend je nach Ergbnis eine andere Funktion aufgerufen.
var floatValue = parseFloat(toFloat);
if (isNaN(floatValue)) {
notFloat();
} else {
isFloat();
}
Die Funktionen parseInt und parseFloat
Die zwei "parse" Funktionen, parseInt und parseFloat, liefern einen numerischen Wert, wenn ihnen eine Zeichenkette als Argument übergeben wird.
Syntax von parseFloat:
parseFloat(str);
wobei parseFloat das Argument str
verarbeitet und
versucht, es in eine Gleitkommazahl umzuwandeln. Falls das Argument ein
anderes Zeichen als ein Vorzeichen (+ oder -), eine Ziffer (0-9), einen
Dezimalpunkt oder einen Exponenten enthält, liefert tdie Funktion den
Wert bis zu dieser Stelle und ignoriert dieses und alle darauffolgenden
Zeichen. Kann das erste Zeichen nicht zu einer Zahl konvertiert werden,
gibt die Funktion NaN zurück.
Syntax von parseInt:
parseInt(str [, radix]);
Die Funktion parseInt verarbeitet die Zeichenkette str
und versucht sie in eine Ganzzahl der Basis, die nach dem Argument durch
Komma getrennt angegeben wird, umzuwandeln. Zum Beispiel bewirkt eine
Basis von 10, dass zu einer Dezimalzahl konvertiert wird; bei einer
Basis von acht zu einer Oktalzahl und bei 16 zu einer Hexadezimalzahl.
Für Basiszahlen größer zehn, werden Buchstaben des Alphabets eingesetzt,
z.B. werden bei hexadezimalen Zahlen die Buchstaben A-F verwendet.
Falls in der Zeichenkette ein anderes Zeichen als ein numerischer Wert der spezifizierten Basis vorkommt, wird dieses Zeichen und alle darauffolgenden Zeichen ignoriert. Wenn das erste Zeichen nicht zu einer Zahl der spezifizierten Basis konvertiert werden kann, gibt die Funktion NaN zurück.
Number- und String-Funktionen
Mit den Number- und String-Funktionen kann ein Objekt zu einer Zahl oder einer Zeichenkette umgewandelt werden.
Die Syntax ist wie folgt:
var objRef;
objRef = Number(objRef);
objRef = String(objRef);
wobei objRef eine Objekt-Referenz ist.
Die Funktion Number
benutzt die Methode valueOf() des Objekts; die Funktion String greift auf die Methode toString() des Objekts zurück.
Das folgende Beispiel konvertiert das Date-Objekt zu einer lesbaren Zeichenkette.
var D = new Date(430054663215),
x;
x = String(D); // x equals "Thu Aug 18 04:37:43 GMT-0700 (Pacific Daylight Time) 1983"
Das folgende Beispiel konvertit ein String-Objekt zu eienem Number-Objekt.
var str = "12",
num;
num = Number(str);
Mit Hilfe des typeof-Operator kann das Ergebnis überprüft werden:
var str = "12",
num;
document.write(typeof str);
document.write("");
num = Number(str);
document.write(typeof num);
Escape- und Unescape-Funktionen
Die Escape- und Unescape-Funtionen funktionieren nicht ordnungsgemäß mit Zeichen, die nicht im ASCII-Zeichensatz enthalten sind und gelten als veraltet. Ab JavaScript 1.5 sollte man stattdessen die Funktionen encodeURI, decodeURI, encodeURIComponent und decodeURIComponent benutzen.
Die Funktionen escape und unescape lassen sich Zeichenketten de- und enkodieren. Die Escape-Funktion gibt eine hexadezimal kodierte Zahl des Arguments aus dem Latin-1 Zeichensatz zurück. Die Unescape-Funktion liefert einen ASCII-String eines als hexadezimal kodierten Werts, der als Argument übergeben wird.
Syntax:
escape(string);
unescape(string);
Diese Funktionen werden primär bei serverseitigem JavaScript eingesetzt, um Name/Wert-Paare zu URLs zu de- oder encodieren.