Objekthierarchien selbst definieren
Thomas
- javascript
Hi JavaScript-Experten!
Ich würde gerne Objekthierarchien definieren; aber was beispielsweise mit VB ein Kinderspiel ist, will mir mit JavaScript nicht so recht gelingen. Das Problem sehe ich in den Arrays.
Beispiel:
function Book(Titel, Autor) {
this.Titel = Titel;
this.Autor = Autor;
this.Kapitel = new Array();
}
function Chapter(Ueberschrift) {
this.Ueberschrift = Ueberschrift;
}
var Buch = new Array();
Buch[12] = new Book("SELFHTML", "Stefan Münz");
Buch[12].Kapitel[2] = new Chapter();
Buch[12].Kapitel[2].Ueberschrift = "HTML";
Buch[12].Kapitel[5] = new Chapter();
Buch[12].Kapitel[5].Ueberschrift = "JavaScript";
Dieser Code funktioniert zwar; bei einer umfangreicheren Objekthierarchie finde ich die ewige Instanzierung (z.B. = new Chapter()) doch lästig.
Kann man das vielleicht auch im Konstruktor unterbringen? In VB würde ich Collection-Objekte erzeugen; also z.B. Chapters vom Typ Chapter.
Mein *Wunsch*code würde z.B. so aussehen:
var MyBooks = new Books();
MyBooks[12].Titel = "SELFHTML";
MyBooks[12].Autor = "Stefan Münz";
MyBooks[12].Chapters[2].Ueberschrift="HTML";
MyBooks[12].Chapters[5].Ueberschrift="JavaScript";
Möglich?
Wie müßten die Konstruktoren dafür aussehen??
Für Eure Antworten bin ich dankbar; auch für den Fall, dass sie "in JavaScript generell NICHT möglich" lautet!
Thomas
Grüssi,
Ich würde gerne Objekthierarchien definieren; aber was beispielsweise mit VB ein Kinderspiel ist, will mir mit JavaScript nicht so recht gelingen. Das Problem sehe ich in den Arrays.
Beispiel:
function Book(Titel, Autor) {
this.Titel = Titel;
this.Autor = Autor;
this.Kapitel = new Array();
}
function Chapter(Ueberschrift) {
this.Ueberschrift = Ueberschrift;
}
var Buch = new Array();
Buch[12] = new Book("SELFHTML", "Stefan Münz");
Buch[12].Kapitel[2] = new Chapter();
Buch[12].Kapitel[2].Ueberschrift = "HTML";
Buch[12].Kapitel[5] = new Chapter();
Buch[12].Kapitel[5].Ueberschrift = "JavaScript";
Dieser Code funktioniert zwar; bei einer umfangreicheren Objekthierarchie finde ich die ewige Instanzierung (z.B. = new Chapter()) doch lästig.
äähhm, meintest du vielleicht so etwas in diese Richtung:
// Konstruktor für das Buch
function Book(Titel, Autor) {
this.Titel = Titel;
this.Autor = Autor;
this.Kapitel = new Array();
}
// "Pseudo-Konstruktor" für die Kapitel
function addChapter(Index, Ueberschrift) {
this.Kapitel[Index] = Ueberschrift;
}
// Und auch ne Methode zum ausgeben:
function getChapter (Index) {
return this.Kapitel[Index];
}
// Die Funktion, zu einer Instanzmethode machen
Book.prototype.addChapter = addChapter;
Book.prototype.getChapter = getChapter;
// Aufruf:
b = new Book("SELFHTML","Stefan Münz");
b.addChapter(1,"Editorial");
b.addChapter(2,"Einführung");
// Test:
alert(b.getChapter(1));
alert(b.getChapter(2));
Es wär zwar schöner ein Array von Chapter-Objekten im Book-Konstruktor anzulegen, es fällt mir aber auf die schnelle nicht wirklich was dazu ein ;-)
hth,
regenfeld
Noch ne kleine Änderung:
function addChapter(index, ueberschrift, start_seite) {
this.Kapitel[index] = new Chapter(ueberschrift,start_seite);
}
function Chapter (ueberschrift,start_seite) {
this.ueberschrift=ueberschrift;
this.start_seite=start_seite;
}
wäre schöner, denn dann hast wirklich ein eigenes Objekt "Chapter", das dann auch mehr als nur eine Variable haben kann. Somit wird auch der "Aufruf" so wie du ihn anfangs haben wolltest:
b.Kapitel[1].addChapter(1,"Editorial", "Seite 1");
alert (b.Kapitel[1].ueberschrift);
alert (b.Kapitel[1].start_seite);
würde allerdings trotzdem zu get-Methoden raten ;-)
lg regenfeld
... wäre schöner, denn dann hast wirklich ein eigenes Objekt "Chapter", das dann auch mehr als nur eine Variable haben kann. Somit wird auch der "Aufruf" so wie du ihn anfangs haben wolltest: ...
Wärend ich noch beim Tippen war (siehe voriges Posting), bist Du mir mit diesem Vorschlag zuvor gekommen!
Thanx again!!!!
Thomas
äähhm, meintest du vielleicht so etwas in diese Richtung:
...
Genau: So etwas in diese Richtung!
Die Idee mit dem Pseudo-Konstruktor hat mir gefehlt.
Vielen Dank für die rasche Antwort!!!!!
Die Lösung mit der ich nun leben kann (sofern in diesem Thread nicht noch tollere Ideen kommen ;-) sieht in etwa so aus:
// Konstruktor für das Buch
function Book(Titel, Autor) {
this.Titel = Titel;
this.Autor = Autor;
this.Kapitel = new Array();
this.addChapter = addChapter;
}
function Chapter(Ueberschrift) {
this.Ueberschrift = Ueberschrift;
}
function addChapter(Ueberschrift, Index) {
if (Index==null) Index=this.Kapitel.length;
this.Kapitel[Index] = new Chapter(Ueberschrift);
}
// Aufruf:
b = new Book("SELFHTML","Stefan Münz");
b.addChapter("Editorial",1);
b.addChapter("Einführung");
b.addChapter("HTML","drei");
// Test:
alert(b.Kapitel[1].Ueberschrift);
alert(b.Kapitel[2].Ueberschrift);
alert(b.Kapitel["drei"].Ueberschrift);
gruss Thomas,
die verwendung eines prototype-objektes, wie es
regenfeld alias Bernhard Peissl aufzeigte, ist bei
umfangreicher objektorientierter programmierung in
JavaScript sehr elegent und (weil) speicherplatzschonend;
ich zitiere aus meiner JavaScript-Bibel, da ich es selber
nicht in anschaulichere worte zu fassen vermag -
(ISBN 3-930673-56-8) /
O'REILLY - JavaScript Das umfassende Referenzwerk -
Seite 110 / Kapitel: 7.4 Objektprototypen:
ab NNAV 3 und MSIE 3 "... gibt es noch einen anderen Weg, die
Methoden, Konstanten und sonstigen Eigenschaften festzulegen,
die von allen Objekten einer Klasse unterstuetzt werden sollen.
Diese Technik besteht darin, die Methoden und sonstigen Eigen-
schaften in einem PROTOTYPEOBJEKT fuer die Klasse zu definieren.
Ein Prototypeobjekt ist ein besonderes Objekt, das mit der Konst-
ruktorfunktion einer Klasse verbunden ist ..."
bei regenfeld: "Book.prototype.addChapter"
// Die Funktion, zu einer Instanzmethode machen
Book.prototype.addChapter = addChapter;
Book.prototype.getChapter = getChapter;
// Konstruktor für das Buch
function Book(Titel, Autor) {
this.Titel = Titel;
this.Autor = Autor;
this.Kapitel = new Array();
}
// "Pseudo-Konstruktor" für die Kapitel
function addChapter(Index, Ueberschrift) {
this.Kapitel[Index] = Ueberschrift;
}
// Und auch ne Methode zum ausgeben:
function getChapter (Index) {
return this.Kapitel[Index];
}
"... und eine wichtige
Besonderheit aufweist: Jede Eigenschaft, die fuer das Prototype-
objekt einer Klasse definiert wird, erscheint wie eine Eigenschaft
eines jeden Objekts dieser Klasse. Dies gilt fuer alle Eigen-
schaften - unabhaengig davon, ob sie dem Prototyp vor oder nach
der Definition der einzelnen Objekte hinzugefuegt werden. Die
Eigenschaften des Prototypobjekts einer Klasse werden von allen
Objekten dieser Klasse gemeinsam genutzt (d.h., die einzelnen
Objekte erhalten keine eigene Kopie der Prototypeigenschaften,
so dass der Speicherbedarf sehr gering bleibt)."
noch wichtiger:
"Die Eigenschaften des Prototypobjekts einer Klasse koennen ueber
alle Objekte der Klasse ausgelesen werden. Auch wenn Sie Eigen-
schaften dieser Objekte zu sein scheinen, sind sie dies nicht
wirklich. Es gibt nur ein Exemplar jeder Prototypeigenschaft, und
dieses Exemplar wird von allen Objekten einer Klasse geteilt. Wenn
Sie eine dieser Eigenschaften eines Objekts 'lesend' ansprechen,
lesen sie also tatsaechlich den gemeinsamen Wert aus dem Prototyp-
objekt ..." ACHTUNG "... 'Setzen' Sie andererseits den Wert
einer solchen Eigenschaft fuer ein bestimmtes einzelnes Objekt,
erzeugen Sie tatsaechlich eine neue Eigenschaft dieses einen
Objekts. Von nun an 'ueberlagert' oder versteckt die neu angelegte
Eigenschaft die gemeinsame Eigenschaft des Prototypobjekts - aber
nur in diesem einen Objekt."
das Fazit der Autoren zu diesem Kapitel lautet:
"Weil Prototypeigenschaften von allen Objekten einer Klasse gemein-
sam benutzt werden, ist es im allgemeinen sinnvoll, sie zur Fest-
legung von Eigenschaften zu benutzen, die fuer alle Objekte einer
Klasse identisch sind. Deswegen kann man mit ihnen auf geradezu
ideale Weise Methoden definieren. ... Wenn Ihre Klasse eine
Eigenschaft mit einem sehr gebraeuchlichen Standardwert definiert,
koennen Sie diese Eigenschaft samt ihrem Standardwert abenfalls
im Prototypobjekt definieren. Dann koennen die wenigen Objekte,
die von dem Standardwert abweichen sollen, ihr eigenes, nicht
gemeinsames Exemplar der Eigenschaft erhalten. Diesem Exemplar
kann dann jeweils ein vom Standard abweichender Wert zugewiesen
werden."
unter der folgenden URL findest Du einen Feature-Artikel, der
eine eigene JavaScript-klasse zum einfacheren dynamischen
erzeugen von HTML-tabellen-code beschreibt - aus heutiger
sicht ist der code nicht allzu elegant, aber sehr interessant
ist die tatsache, dass im klassen-konstruktor zur laufzeit
nur die eigenschaften einer tabelle initialisiert werden, die
auch tatsaechlich benutzt werden sollen;
http://aktuell.de.selfhtml.org/artikel/javascript/table-obj/index.htm
by(t)e by(t)e - peterS. - pseliger@gmx.net
die verwendung eines prototype-objektes, wie es
regenfeld alias Bernhard Peissl aufzeigte, ist bei
umfangreicher objektorientierter programmierung in
JavaScript sehr elegent und (weil) speicherplatzschonend;
Vielen Dank für die wirklich ausführliche Erklärung!!!
Wenn ich noch mal zusammenfassen darf:
Methoden besser mit *prototype* definieren und nicht direkt im Konstruktor.
Über die Begründung werde ich bei Gelegenheit nochmals meditieren ;-)
Thanx!
Grüssi,
Wenn ich noch mal zusammenfassen darf:
Methoden besser mit *prototype* definieren und nicht direkt im Konstruktor.
Nunja, nochmal andersrum: Deine Klasse Book erbt quasi per default properties bzw. funktionen von seinem prototype-Objekt. Alles was im prototype "drin steht" steht allen Book-Objekten (gleichermassen) zur Verfügung. Wenn du nur VB kannst wird dir das nicht viel sagen, aber in Java würd sich das ca so lesen:
class Book extends prototype {
...
}
oder so ähnlich, nur dass nicht explizit vererbt werden muss!
Beispiel:
function Circle {x,y,r} {
this.x=x; this.y=y; this.r=r;
}
Circle.prototype.pi = 3.1415;
Somit teilen Sich alle Circle Objekte diese Variable 'pi'. Wenn nun eine Klasse keine pi-Variable definiert, schaut der Javascript-Interpreter ob im prototype eine pi-Variable ist. Allerdings kann man diese pi auch in der Klasse überschreiben.
Kurz gesagt *g* Das prototype-Objekt ist sowas wie ein Defaultkonstruktor, in dem Variablen/Funktionen vorgegeben werden können.
Es können übrigens auch andere Objekte prototypes haben. Du kannst beispielsweise eine Spezial-methode für alle String-Objekte einführen:
function myEndsWith ( c ) {
return ( c == this.charAt( this.length - 1 ) )
}
String.prototype.endsWith = myEndsWith;
var message = "huhu"
message.endsWith(h); // gibt false zurück
message.endsWith(u); // gibt true zurück
Über die Begründung werde ich bei Gelegenheit nochmals meditieren ;-)
besser: iterieren ;-)
lg regenfeld
Ach ich Schussel, hab ich doch erst im lesen des eigenen Beitrags gesehen was ich da übersehen habe ;-)
Hier sieht man schön den Unterschied:
function Circle {x,y,r} {
this.x=x;
this.y=y;
this.r=r;
}
Circle.prototype.pi = 3.1415;
und vergleiche jetzt:
function Circle {x,y,r} {
this.x=x;
this.y=y;
this.r=r;
this.pi=3.1415;
}
Das wär intuitiv sicher der erste Lösungsansatz gewesen, oder? 'pi' einfach als Instanzvariable zu definieren. Aber was passiert nun bei letzterer Variante? Bei *jedem* neuen Objekt (c = new Circle()) wird zusätzlich zu x , y , und r - die bei jedem Objekt sicherlich andere Werte haben - auch noch ein pi mit festem Wert initialisiert, völlig unnütz, da es sowieso für alle Objekte gleich ist !!
Stell dir vor du willst die einen Bildschirmschoner basteln und ein paar Kreise ausgeben, die sich über den Bildschirm räkeln, sagen wir ca. 100.000.000 ;-)
Hoffe die Sache ist nun etwas klarer ;-)
lg regenfeld
Nunja, nochmal andersrum: Deine Klasse Book erbt quasi per default properties bzw. funktionen von seinem prototype-Objekt. Alles was im prototype "drin steht" steht allen Book-Objekten (gleichermassen) zur Verfügung. Wenn du nur VB kannst wird dir das nicht viel sagen, aber in Java würd sich das ca so lesen:
*nur* VB?!?! ;-)
Die Sprache ist besser als ihr Ruf! Auch wenn in der Version 6 noch einige Sachen fehlen.
Die Erklärung mit Java hilft mir nicht ganz weiter, aber auf C# umgelegt, handelt es sich dann offensichtlich um ein statisches Mitglied der Klasse, richtig?
Ich war mir über die Bedeutung von prototype bis jetzt nicht ganz im Klaren; dachte, es dient nur dazu, um vorhandene Objekte um eigene Member zu erweitern. (vgl. 7.8, "JavaScript" von Stefan Koch, dpunkt.verlag)
Danke, Jungs für Eure Erklärungen, die über meine eigentliche Fragestellung weit hinaus gegangen sind!! Eigentlich sollte dieser Thread für die Nachwelt im Archiv erhalten bleiben, oder?
Über die Begründung werde ich bei Gelegenheit nochmals meditieren ;-)
besser: iterieren ;-)
Wenn ich nicht ganz am Holzweg bin, dann hab ich das doch recht schnell gecheckt, oder?!
Hallo Thomas
*nur* VB?!?! ;-)
ich hab gewusst dass das kommt *fg* aber es war kein wertendes "und" nur die Feststellung, dass es dir nix sagen wird, wenn du ausser VB keine andere Programmiersprache kennst ;-)
Die Erklärung mit Java hilft mir nicht ganz weiter, aber auf C# umgelegt, handelt es sich dann offensichtlich um ein statisches Mitglied der Klasse, richtig?
Nun, es drückt eine Vererbungsbeziehung aus. Es erbt Methoden und Variablen von der Mutterklasse. jawa.awt.TextArea erbt beispielweise implizit (d.h. ohne dass du die Vererbung im Code niederschreiben musst) von java.awt.Component. Diese Component Klasse stellt dann Methoden zur Verfügung, um einen Objekt im Raum anzuordnen ( move() zum Bleistift ). Diese Move-Methode kann dann jedes Objekt verwenden, ohne sie explizit innerhalb seiner Klassendefinition zu spezifizieren. Somit ist eine Methode für das Herumschieben von Textfeldern, Buttons, Checkboxes, was auch immer verantwortlich. Ansonsten müsstest du in jeder klasse eine move()-Methode definieren ;-)
Ich war mir über die Bedeutung von prototype bis jetzt nicht ganz im Klaren; dachte, es dient nur dazu, um vorhandene Objekte um eigene Member zu erweitern. (vgl. 7.8, "JavaScript" von Stefan Koch, dpunkt.verlag)
Genau das tuts ja im Grunde, ich hab mich nur ein bissl kompizierter ausgedrückt ;-)
Eigentlich sollte dieser Thread für die Nachwelt im Archiv erhalten bleiben, oder?
Wird er ja auch, das Voting-System wurde ja praktisch erfolgreich geputscht ;-) Bisher wurde alles archiviert, und wenn sich da nix dran geändert hat, kommt auch dieser Thread ins Archiv!
Über die Begründung werde ich bei Gelegenheit nochmals meditieren ;-)
besser: iterieren ;-)
Wenn ich nicht ganz am Holzweg bin, dann hab ich das doch recht schnell gecheckt, oder?!
jop. :o)
lg regenfeld
Hallo,
anstatt solche Weisheiten für Dich zu behalten solltest Du Dich gleich ans Werk machen und einen ausgedehnten Featuere-Artikel schreiben, damit die anderen auch was davon haben ;). Das ist kein Joke, soetwas fehlt hier im Selfraum wirklich (finde ich).
Mein Beitrag zur prototypischen Diskussion :
In JavaScript kann eine klassenbasierende Vererbung nicht umgesetzt werden, da es keine Klassen gibt, denn es fehlt an ausreichender Typisierung. Zudem können Objekte zur Laufzeit um Methoden und Eigenschaften beliebig erweitert werden. Dies sind zwei wesentliche Aspekte, warum JS kein Klassenkonzept kennt. Dennoch läßt sich über die prototypische Vererbung (von der Stanford Univerity für die Programmiersprache self entwickelt) eine Generalisierung und Spezialisierung umsetzen. Prototypische Objekte werden durch die Konstruktor-Methode new erzeugt. Dabei enthält das neu erzeugte Objekt lediglich einen Verweis auf den Prototypen. Prototypische Vererbung wird realisiert, indem man den prototypischen Verweis des Prototypen auf ein prototypisches Objekt zeigen läßt. Da es jeweils nur einen prototypischen Verweis gibt, sind Mehrfachvererbungen ausgeschlossen.
function Tier() //- Prototyp Tier
{
this.belle = belle; //- Methode
}
Mensch.prototype = new Tier();
function Mensch() //- Prototyp Mensch
{
}
var ich = new Mensch(); //- Objekt ich besitzt nur einen prototypischen Verweis (auf Mensch);
ich.belle();
Erklärung :
belle ist keine Methode des Objektes ich. Das Objekt ich besitzt lediglich einen Verweis auf den Prototypen Mensch (und der wiederum auf Tier). Der Interpreter sucht im Protypen Mensch nach der Methode belle. Wird er nicht fündig, so zieht er den prototypischen Verweis von Mensch, der auf den Prototypen Tier zeigt, für die Anforderungsuntersuchung heran.
Im IE kann über den operator instanceof ermittelt werden, ob ein Objekt von einem bestimmten Prototypen erbt.
if (ich instanceof Tier)
alert("ich stamme vom Tier ab");
Beim NS steht dieser Operator nicht zur Verfügung. Hier muß eine Funktion geschrieben werden, welche sämtliche prototypische Verweise auf Vererbungsabhängigkeiten hin untersucht. Dazu kann direkt auf den prototypischen Verweis __proto__ zugegriffen werden.
function instanceof(obj, proto)
{
while (obj != null)
{
if (obj == proto.prototype)
return true;
else
obj = obj.__proto__;
}
}
if (instanceof(ich, Mensch))
alert("Laut NS stamme ich vom Tier ab");
Siehe auch vergleichende Lektüre : Internet World 08/2001 S. 86 die Macht der Objekte von Cai Ziegler.
gruß
tim