Die Sprache JavaScript wurde nach einem einfachen objektbasierten Paradigma gestaltet. Ein Objekt besteht aus einer Zusammenstellung von Eigenschaften. Eine Eigenschaft ist eine Verknüfung eines Namens mit einem Wert und der Wert einer Eigenschaft kann auch eine Funktion sein. Eine Funktion, die einem Objekt zugeordnet ist, nennt man Methode. Zusätzlich zu den Objekten, die im Browser vordefiniert sind, können auch eigene Objekte erstellt werden.
Dieses Kapitel beschreibt, wie Objekte, Eigenschaften, Funktionen und Methoden benutzt werden und wie man eigene Objekte erstellt.
Übersicht zu Objekten
Objekte in JavaScript können, wie auch bei anderen Programmiersprachen, mit Objekten in der realen Welt verglichen werden. Daher können normale Gegenstände zur Veranschaulichung des Konzepts dienen.
Bei JavaScript ist ein Objekt eine eigenständige Einheit mit Eigenschaften und einem Typ. Ein Objekt könnte z. B. eine Tasse sein, denn eine Tasse ist ein Objekt mit bestimmten Eigenschaften. Sie hat eine Farbe, ein Design, ein Gewicht, ist aus einem bestimmten Material gefertigt usw. Auf dieselbe Art und Weise besitzen JavaScript-Objekte Eigenschaften, womit deren Merkmale definiert werden.
Objekte und Eigenschaften
Ein JavaScript-Objekt hat Eigenschaften, die mit dem Objekt verknüpft sind. Objekteigenschaften sind im Prinzip dasselbe, wie normale Variablen, außer dass sie zusätzlich einem Objekt zugeordnet sind. Der Zugriff auf eine Eigenschaft kann mit Hilfe der Punkt-Notation erfolgen:
objectName.propertyName
Wie auch bei normalen Variablen gilt es sowohl bei Objektnamen als
auch bei deren Eigenschaften zwischen Groß- und Kleinschreibung zu
unterscheiden. Eine Eigenschaft wird definiert, indem man dieser
Eigenschaft einen Wert zuweist. Wir erstellen zum Beispiel ein Objekt
mit dem Namen myCar
and geben diesem Objekt drei verschiedene Eigenschaften make
, model
und year
:
var myCar = new Object();
myCar.make = "Ford";
myCar.model = "Mustang";
myCar.year = 1969;
Eigenschaften von JavaScript-Objekten können auch mit Hilfe der
Klammer-Notation definiert und angesprochen werden. Objekte werden
manchmal assoziative Arrays genannt, da jede Eigenschaft mit
einem String-Wert verknüpft ist, über den auf die Eigenschaft
zugegriffen werden kann. Zum Beispiel können die Eigenschaften des
Objekts myCar
wie folgt definiert werden:
myCar["make"] = "Ford";
myCar["model"] = "Mustang";
myCar["year"] = 1969;
Der Name einer Objekteigenschaft kann jeder beliebige JavaScript-String und auch alles sein, was zu einem String konvertiert werden kann, einschließlich der leere String. Jedoch lassen sich Eigenschaftsnamen, die keine gültigen JavaScript-Bezeichner sind (z. B. Eigenschaftsnamen, die Leerzeichen oder Bindestriche enthalten oder mit einer Ziffer beginnen) nur über die Klammer-Notation ansprechen. Diese Notation ist außerdem sehr nützlich, wenn Eigenschaftsnamen dynamisch festgelegt werden, also wenn der Name der Eigenschaft erst während der Laufzeit festgelegt wird.
Ein paar Beispiele:
var myObj = new Object(),
str = "myString",
rand = Math.random(),
obj = new Object();
myObj.type = "Dot syntax";
myObj["date created"] = "String with space";
myObj[str] = "String value";
myObj[rand] = "Random Number";
myObj[obj] = "Object";
myObj[""] = "Even an empty string";
console.log(myObj);
Der Eigenschaftsname kann auch in einer Variablen gespeichert und die Eigenschaft dann über diese Variable angesprochen werden:
var propertyName = "make";
myCar[propertyName] = "Ford";
propertyName = "model";
myCar[propertyName] = "Mustang";
Mit Hilfe der Klammer-Notation in Kombination mit for...in ist es möglich, über alle zählbaren Eigenschaften eines Objekts zu iterieren. Die folgende Funktion gibt alle Eigenschaften eines Objekts aus. Das Objekt und dessen Name werden als Argumente übergeben.
function showProps(obj, objName) {
var result = "";
for (var i in obj) {
if (obj.hasOwnProperty(i)) {
result += objName + "." + i + " = " + obj[i] + "\n";
}
}
return result;
}
Der Funktionsaufruf showProps(myCar, "myCar")
würde folgendes zurückgeben:
myCar.make = Ford myCar.model = Mustang myCar.year = 1969
Fast alles ist ein Objekt
Bei JavaScript ist fast alles ein Objekt. Alle einfachen Typen, mit Ausnahme von null
und undefined
,
werden wie Objekte behandelt. Sie können zugewiesene Eigenschaften sein
(zugewiesene Eigenschaften mancher Typen sind nicht beständig) und
haben alle Merkmale von Objekten.
Durchlaufen von Objekteigenschaften
Ab ECMAScript 5 gibt es drei verschiedene Möglichkeiten, um Objekteigenschaften aufzulisten oder zu durchlaufen:
- for...in-Schleifen
Diese Methode durchläuft alle aufzählbaren Eigenschaften eines Objekts und seines Prototypes. - Object.keys(o)
Diese Methode gibt ein Array mit allen eigenen aufzählbaren Eigenschaftsnamen (Schlüssel) eines Objektso
zurück. - Object.getOwnPropertyNames(o)
Diese Methode gibt ein Array mit allen Eigenschaftsnamen (aufzählbar oder nicht) eines Objektso
zurück.
Bei ECMAScript 5 gibt es keine vordefinierte Funktion, um alle Eigenschaften eines Objekts zu durchlaufen. Hier kann die folgende Funktion aushelfen:
function listAllProperties(o){
var objectToInspect;
var result = [];
for(objectToInspect = o; objectToInspect !== null; objectToInspect = Object.getPrototypeOf(objectToInspect)){
result = result.concat(Object.getOwnPropertyNames(objectToInspect));
}
return result;
}
Das Durchlaufen der Eigenschaften kann nützlich sein, um "versteckte" Eigenschaften aufzudecken (Eigenschaften des Prototype, die nicht über das Objekt ansprechbar sind, weil eine andere frühere Eigenschaft des Prototype denselben Namen hat). Es können auch nur die ansprechbaren Eigenschaften aufgelistet werden, indem einfach doppelte Namen aus dem Array entfernt werden.
Erstellung von neuen Objekten
Bei JavaScript sind eine Reihe von Objekten bereits vordefiniert.
Zusätzlich können eigene Objekte erstellt werden. Bei JavaScript 1.2 und
neuer können Objekte mit Hilfe eines Objekt-Literals erstellt werden.
Alternativ kann auch zuerst eine Konstruktorfunktion erstellt werden und
dann Instanzen von Objekten mit Hilfe dieser Funktion und dem new
-Operator erstellt werden.
Benutzung von Objekt-Literalen
Auch mit Hilfe von Objekt-Literalen (auch Objekt-Initialisierer genannt) können Objekte erstellt werden. Die Benutzung von Objekt-Literalen wird als die "buchstäbliche" Notation zum Erstellen von Objekten bezeichnet. Die Bezeichnung Objekt-Initialisierung ist gleichbedeutend mit der Terminologie bei der Programmiersprache C++.
Hier die Syntax für ein Objekt, das über einen Objekt-Literal erstellt wird:
var obj = { property_1: value_1, // property_# may be an identifier...
2: value_2, // or a number...
// ...,
"property n": value_n }; // or a string
wobei obj
der Name des Objekts ist, jedes property_i
eine Eigenschaft bzw. ein Bezeichner (Name, Zahl, oder String) und jedes value_i
ein Ausdruck, dessen Wert der jeweiligen Eigenschaft zugewiesen ist. Die Variable obj
und die Zuweisung ist optional; wenn nicht an anderer Stelle auf das
Objekt verwiesen werden soll, muss die Variable nicht erstellt werden.
(Achtung: Wenn der Code an einer Stelle steht, wo eine Anweisung
erwartet wird, muss er in Klammern eingeschlossen werden, damit keine
Verwechslung mit einer Blockanweisung stattfindet).
Wenn ein Objekt mit Hilfe eines Objekt-Literals in einem Top-Level-Script erstellt wird, interpretiert JavaScript das Objekt jedesmal, wenn ein Ausdruck ausgewertet wird, der das Objekt-Literal enthält. Befindet sich der Code für das Objekt-Literal innerhalb einer Funktion, wird es bei jedem Funktionsaufruf erstellt.
Die folgende Anweisung erstellt ein Objekt und weist es der Variablen x
zu, falls (und nur dann) die Bedingung cond
zu true
evaluiert.
if (cond) var x = {hi: "there"};
Das folgende Beispiel zeigt die Erstellung das Objekts myHonda
mit drei Eigenschaften. Die Eigenschaft engine
ist ebenfalls ein Objekt mit eigenen Eigenschaften.
var myHonda = {color: "red", wheels: 4, engine: {cylinders: 4, size: 2.2}};
Auch mit Objekt Literalen können Arrays erstellt werden, siehe Array Literale.
Bei JavaScript 1.1 und früher können Objekt-Literale nicht eingesetzt werden. Objekte können nur mit Hilfe der Konstruktorfunktionen oder einer Funktion, die durch ein anderes Objekt bereitgestellt wird und diese Aufgabe erledigt, erstellt werden (mehr dazu im nächster Abschnitt).
Benutzung einer Konstruktor-Funktion
Alternativ kann ein Objekt über die beiden folgenden Schritte erstellt werden:
Definition des Objekttyps durch Schreiben einer Konstruktorfunktion.
Es gibt eine Übereinkunft, dass man für diese Funktion einen großen
Anfangsbuchstaben verwendet. Erstellen einer Instanz des Objekts mit
Hilfe von new
.
Zur Definition des Objekttyps wird eine Funktion für diesen Typ
erstellt, welche den Namen, die Eigenschaften und Methoden enthält.
Angenommen es soll ein Objekttyp für Automobile erstellt werden. Das
Objekt soll car
heißen und die drei Eigenschaften make
, model
und year
besitzen. Dann definiert man eine Funktion Car
, welche die Werte für die Eigenschaften als Parameter entgegennimmt:
function Car(make, model, year) {
this.make = make;
this.model = model;
this.year = year;
}
Bei dieser Funktion wird das Schlüsselwort this
eingesetzt, um den Eigenschaften des Objekts die Werte der Argumente zuzuweisen.
Anschließend kann mit Hilfe dieser Funktion ein Objekt mycar erstellt werden:
var mycar = new Car("Eagle", "Talon TSi", 1993);
Mit dieser Zuweisung wird das Objekt mycar
erstellt und die Argumentwerte werden den Eigenschaften zugewiesen. Die Eigenschaft mycar.make
bekommt den Wert "Eagle", mycar.year
den Wert 1993 usw.
Über den Aufruf von new
können beliebig viele Objekte erstellt werden. Zum Beispiel:
var kenscar = new Car("Nissan", "300ZX", 1992);
var vpgscar = new Car("Mazda", "Miata", 1990);
Ein Objekt kann eine Eigenschaft haben, die selbst wieder ein Objekt ist. Angenommen man erstellt ein Objekt Person
wie folgt:
function Person(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
Und erzeugen dann zwei Instanzen dieses Objekts:
var rand = new Person("Rand McKinnon", 33, "M");
var ken = new Person("Ken Jones", 39, "M");
Dann kann man die Funktion Car
erweitern, damit das Objekt eine weitere Eigenschaft owner besitzt, der ein Person-Objekt zugewiesen werden kann:
function Car(make, model, year, owner) {
this.make = make;
this.model = model;
this.year = year;
this.owner = owner;
}
Nun können Instanzen des Objekts wie folgt erzeugt werden:
var car1 = new Car("Eagle", "Talon TSi", 1993, rand);
var car2 = new Car("Nissan", "300ZX", 1992, ken);
Hierbei wern nun nicht nur String- und Integer-Werte, sondern auch jeweils ein Besitzer-Objekt (rand
und ken
) an die Funktion übergeben. Den Besitzer von car1
kann man nun z. B. über folgende Eigenschaft herausfinden:
car2.owner.name
Eigenschaften können jederzeit zu einem bereits vordefinierten Objekt hinzugefügt werden. Diese Zuweisung:
car1.color = "black";
fügt dem Objekt car1 die Eigenschaft color
mit dem Wert "black"
hinzu. Dies betrifft allerdings keine der anderen Instanzen. Um allen
Instanzen die neue Eigenschaft hinzuzufügen, muss die Definition des car
Objekttyps erweitert werden.
Benutzung der Methode Objekt.create
Objekte können auch mit Hilfe der Methode Object.create
erstellt werden. Dieser Methode kann sehr nützlich sein, da sie erlaubt,
das entsprechende Prototype-Objekt ohne die Definition einer
Konstruktorfunktion auszuwählen. Weitere Informationen findet man in der
Referenz.
Vererbung
Alle Objekte bei JavaScript erben von wenigstens einem anderen Objekt. Das Objekt, von dem geerbt wird, nennt man Prototype. Die vererbten Eigenschaften können im Prototyp-Objekt des Konstruktors gefunden werden.
Indexieren von Objekt-Eigenschaften
Bei JavaScript 1.0 kann über den Namen oder den Index auf die Eigenschaft eines Objekt zugegriffen werden. Bei JavaScript 1.1 und neuer muss der Zugriff auf eine Eigenschaft wieder über den Namen erfolgen, wenn sie über den Namen definiert wurde und über den Index, wenn sie über den Index definiert wurde.
Diese Beschränkung gilt sowohl für Objekte und ihre Eigenschaften,
wenn sie über die Konstruktorfunktion erstellt wurden (wie im vorherigen
Abschnitt beim car-Objekt) als auch wenn verschiedene Eigenschaften
explizit definiert werden (zum Beispiel myCar.color = "red"
). Wenn man anfänglich eine Objekt-Eigenschaft über ihren Index definiert, z. B. myCar[5] = "25 mpg"
, so lässt sich diese Eigenschaft später nur über myCar[5]
ansprechen.
Eine Ausnahme für diese Regel gibt es bei Objekten, die von HTML
stammen, wie z. B. dem forms-Array. Auf diese Objekte kann entweder über
ihre Nummer (basierend darauf, wo sie im Dokument vorkommen) oder ihren
Namen (falls definiert) zugegriffen werden. Wenn im Dokument z. B. das
zweite Formular-Tag <FORM>
ein NAME
-Attribute mit dem Wert "myForm"
hat, kann dieses Formular über document.forms[1],
document.forms["myForm"]
oder document.myForm
angesprochen werden.
Definition von Eigenschaften für einen Objekttyp
Einem zuvor definierten Objekt können mit Hilfe des Prototyp neue Eigenschaften zugeordnet werden. Auf diese Weise wird die Eigenschaften von allen Instanzen geteilt. Der folgende Code fügt allen Objekten vom Typ car eine Farbeigenschaft hinzu und weist der Eigenschaft von cor1 einen Wert zu.
Car.prototype.color = null;
car1.color = "black";
Im Abschnitt zu den Eigenschaften des Function
-Objekts in der JavaScript Referenz findet man weiter Informationen.
Defintion von Methoden
Eine Methode ist eine Funktion die mit einem Objekt verknüpft ist. Anders ausgedrückt: Eine Methode ist eine Eigenschaft eines Objekts, die eine Funktion ist. Methoden werden genauso wie normale Funktionen definiert, außer dass sie einem Objekt zugeordnet werden müssen.
Beispiele:
objectName.methodname = function_name;
var myObj = {
myMethod: function(params) {
// ...do something
}
};
Bei diesem Beispiel ist objectName ein existierendes Objekt, methodname ist der Name der Methode und function_name, der Name einer Funktion die sich später über methodname wie folgt aufrufen lässt:
object.methodname(params);
Methoden können für einen Objekttyp definiert werden, indem sie innerhalb einer Konstruktorfunktion definiert werden. Zum Beispiel knnte man eine Funktion definieren, welche die Eigenschaften des zuvor definierten car-Objekts formatiert und auflistet:
function displayCar() {
var result = "A Beautiful " + this.year + " " + this.make
+ " " + this.model;
pretty_print(result);
}
wobei pretty_print
eine Funktion ist, welche eine horizontale Linie und einen String ausgibt. Beachten Sie die Verwendung des Schlüsselworts this
, womit auf das Objekt verwiesen wird.
Diese Funktion kann man zu einer Methode des Objekts car machen, indem man folgende Zuweisung der Objektdefinition hinzufügt.
this.displayCar = displayCar;
Die ganze Definition des Objekts car ist dann wie folgt:
function Car(make, model, year, owner) {
this.make = make;
this.model = model;
this.year = year;
this.owner = owner;
this.displayCar = displayCar;
}
Anschließend kann man die Methode displayCar
wie folgt aufrufen:
car1.displayCar();
car2.displayCar();
Figure 7.1: Ausgabe der Methode.
Benutzung von this zur Objektreferenzierung
Javascript besitzt das spezielle Schlüsselwort this, das man
innerhalb von Methoden benutzen kann, um auf das aktuelle Objekt zu
verweisen. Wenn man zum Beispiel eine Funktion mit Namen validate
hat, die einen Wert einer Objekteigenschaft validiert und der zwei Grenzwerte als Parameter übergeben werden:
function
validate(obj, lowval, hival) {
if
((obj.value < lowval) || (obj.value > hival))
alert(
"Invalid Value!"
);
}
Dann kann man die Funktion für jedes Formular-Element über den onchange-event-Handler aufrufen und mit Hilfe von this
das Element übergeben:
<input type="text" name="age" size="3" onChange="validate(this, 18, 99)">
Im Allgemeinen verweist this
auf das aufrufende Objekt.
In Kombination mit der Eigenschaft form
kann this
auf das Elternobjekt eines Elements verweisen. Beim folgenden Beispiel enthält das Formular myForm
ein Textobjekt und einen Button. Klickt der Benutzer den Button an,
wird der Name des Formulars als Wert für des Textobjekt gesetzt. Beim
onclick-Event-Handler des Buttons wird mit this.form
auf das Elternobjekt myForm
verwiesen.
Definition von gettern und settern
Ein getter
ist eine Methode, die einen Wert einer spezifischen Eigenschaft bekommt. Ein setter
ist eine Methode, die den Wert einer spezifischen Eigenschaft setzt.
Man kann getter und setter für jedes vordefinierte Kernobjekt oder
benutzerdefinierte Objekt verwenden, welches das Hinzufügen von neuen
Eigenschaften erlaubt. Als Syntax für die Definition von gettern und
settern kommt die literale Syntax zum Einsatz.
Hinweis: JavaScript 1.8.1
Ab JavaScript 1.8.1 werden setter nicht weiter aufgerufen, wenn Eigenschaften in Objekten und Array-Initialisierern gesetzt werden.
Die nachfolgende Sitzung mit JS-Shell veranschaulicht wie getter und setter für ein benutzerdefiniertes Objekt o
funktionieren. Die JS-Shell ist ein Programm, das Entwicklern
ermöglicht, Javascript-Code im Batch-Modus oder interaktiv zu testen.
Bei Firefox kann man die Shell über die Tastenkombination Strg+Shift+K
öffnen.
Die Eigenschaften des Objekts o sind:
o.a
— Eine Zahlo.b
— Ein getter, dero.a
plus 1 zurückgibto.c
— Ein setter, der den Wert von o.a auf den halben Wert von o.c setzt.
Achtung: Funktionsnamen von gettern und settern, die in einem Objekt-Literal mit Hilfe von "[gs]et property()" (anstatt mit __define[GS]etter__
wie unten) definiert werden, sind nicht die Namen des getters/setters selbst, obgleich die [gs]et propertyName(){ }
-Syntax zu dieser Annahme verleitet. Um eine Funktion in einem getter oder setter mit Hilfe der "[gs]et property()"-Syntax zu benennen, definiert man eine explizit benannte Funktion mit Hilfe von Object.defineProperty
(oder dem Object.prototype.__defineGetter__
Fallback).
Die folgende JS-Shell-Sitzung demonstriert, wie getter und setter den
Date-Prototype erweitern, um allen Instanzen des vordefinierten
Date-Objekts eine weitere Eigenschaft year
hinzuzufügen. Die bereits existierenden Methoden getFullYear
und setFullYear
werden benutzt, um der year
-Eigenschaften getter und setter hinzuzufügen.
Mit diesen Anweisungen wird ein getter und setter für die year-Eigenschaft definiert:
js> var d = Date.prototype;
js> d.__defineGetter__("year", function() { return this.getFullYear(); });
js> d.__defineSetter__("year", function(y) { this.setFullYear(y); });
Und so wird auf getter und setter des Date-Objekts zugegriffen:
js> var now = new Date;
js> print(now.year);
2000
js> now.year = 2001;
987617605170
js> print(now);
Wed Apr 18 11:13:25 GMT-0700 (Pacific Daylight Time) 2001
Veraltete Syntax
Bei früheren Versionen unterstütze Javascript einige unterschiedliche Syntaxen für getter und setter, die nicht von anderen Engines unterstützt wurden. Bei neueren Versionen werden diese nicht mehr unterstützt. Weitere Informationen darüber, was genau entfernt und geändert wurde, findet man in diesem Abschnitt.
Zusammenfassung
Im Prinzip können getter und setter wie folgt definiert werden:
- über Objekt-Initializer, oder
- durch späteres hinzufügen zu beliebiger Zeit und einem beliebigen Objekt mit Hilfe von gettern oder settern.
Wenn getter und setter über Objekt-Initialisierer definiert werden, ist alles was man tun muss, dem Methodennamen ein get oder set voranzustellen. Die getter-Methode darf dabei keinen Parameter entgegennehmen, während der setter-Methode exakt ein Paremeter übergeben wird - der neuen Wert, der gesetzt werden soll.
Zum Beispiel:
var o = {
a: 7,
get b() { return this.a + 1; },
set c(x) { this.a = x / 2; }
};
Getter und setter können auch jederzeit mit Hilfe der zwei speziellen Methoden __defineGetter__
und __defineSetter__
hinzugefügt werden. Beide Methoden erwarten den Namen des getter oder
setter als ersten Parameter in Form eines Strings. Der zweite Parameter
ist die Funktion, die als getter oder setter aufgerufen werden soll.
Dazu folgendes Beispiel (anknüpfend an das letzte Beispiel):
o.__defineGetter__("b", function() { return this.a + 1; });
o.__defineSetter__("c", function(x) { this.a = x / 2; });
Für welche der beiden Möglichkeiten man sich entschedet, hängt vom Programmierstil und der zu bewältigenden Aufgabe ab. Wenn man sowieso einen Objekt-Initializer benutzt, um einen Prototype zu definieren, wird man sich eher für die erstgenannte Form entscheiden. Diese ist auch kompakter und einfacher nachvollziehbar. Sofern man getter und setter zu einem späteren Zeitpunkt definiert, etwa weil man den Prototype oder das entsprechende Objekt nicht selbst definiert hat, bleibt nichts anderes übrig, als die zweite Form zu benutzen. Die zweite Form bringt die Dynamik von JavaScript wunderbar zum Ausdruck, kann aber dazu führen, dass der Code schwer lesbar und verständlich ist.
Bei Firefox vor Version 3.0, werden getter und setter nicht für
DOM-Elemente unterstützt. Bei ältere Versionen von Firefox scheitert die
Ausführung ohne eine Fehlermeldung. Wenn Exceptions für diese Browser
benötigt werden, kann man als Workaround den Prototype von HTMLElement(HTMLElement.prototype.__define[SG]etter__)
verändern und eine Ausnahme auslösen.
Weitere Informationen
Löschen von Eigenschaften
Mit Hilfe des delete
-Operators können Eigenschaften gelöscht werden:
//Neues Objekt "myobj" mit zwei Eigenschaften, "a" und "b".
var myobj = new Object;
myobj.a = 5;
myobj.b = 12;
//Löschen der Eigenschaft "a". Das Objekt mit der Eigenschaft "b" bleibt bestehen.
delete myobj.a;
Mit delete
kann man auch globale Variablen löschen, wenn das Schlüsselwort var
bei der Deklaration nicht verwendet wurde:
g = 17;
delete g;
Siehe delete
-Operator.