[code lang=javascript]function clone1(o) {
function c(o) {
for (var i in o) {
this[i] = o[i];
}
}
return new c(o);
}
Die Funktion macht nichts anderes als ein leeres Objekt erzeugen und mit for-in Member rüberkopieren (bzw. bei Objekten referenzieren).
Dafür ist kein Konstruktor und keine Instantiierung nötig, man kann auch einfach ein leeres Objekt mit {} erzeugen.
function clone2(obj) {
if(obj == null || typeof(obj) != 'object') return obj;
var c = new obj.constructor();
for(var key in obj) {
c[key] = clone2(obj[key]);
}
return c;
}
Die Funktion macht alles doppelt, sie erzeugt eine neue Instanz mithilfe des Konstruktors, dann kopiert sie zusätzlich alle Member zu ihm herüber - aber sie ruft sich für jeden Member rekursiv auf. Das macht für ein ganz bestimmten Fall Sinn, nämlich das Kopieren von Object-Strukturen. Aber für Primitives (String, Boolean, Number...) erzeugt es Objects, was absolut Banane ist. Objects werden referenziert, Primitives kopiert. Lass die Finger von solchen Funktionen. Sie sind nicht durchdacht oder gehen von ganz bestimmten Fällen aus.
function charakter() {
var charakter = this;
Hier legst du eine lokale Variable an als Referenz auf die Instanz
charakter.attributeHinzufuegen = function
charakter.werte = function () {
Hier notierst du Closures, die die Variable charakter einschließen
charakter.staerke = staerkeNeu;
charakter.geschick = geschickNeu;
charakter.vitalitaet = vitalitaetNeu;
return new Array(charakter.staerke,charakter.geschick,charakter.vitalitaet);
Hier greifst du auf die eingeschlossene Variable zu. Der Wert der Variable ist der, mit dem du die Variable beim ersten und einzigen Ausführen der Konstruktorfunktion angelegt hast!
var charakter0 = new charakter();
var charakter1 = clone1(charakter0);
charakter0.attributeHinzufuegen(12,2,19);
charakter1.attributeHinzufuegen(14,8,2);
// gibt zweimal „14,8,2“ aus:
alert(charakter0.werte());
alert(charakter1.werte());
Das ist ganz klar: Du arbeitest immer noch mit »privaten Variablen«, d.h. mit Closures!
Du kopierst das Funktionsobjekt werte. Diese ist jedoch eine Closure, die charakter einschließt. Und charakter verweist auf die Instanz, die du hier charakter0 nennst.
Wenn du mal schreiben würdest:
charakter.werte = function () {
alert(charakter === this); // false
alert(charakter === window.charakter0); // true
}
würde das zeigen, dass charakter gar nicht auf die Kopie, sondern die Vorlage verweist.
Wenn du hier auf das Objekt verweisen willst, an dem die Methode hängt und in dessen Kontext sie ausgeführt wird, dann musst du direkt this verwenden. Denn charakter ist eine eingeschlossene Variable, die als Referenz auf die *andere* Instanz angelegt wurde.
Alles in allem würde ich dir Vorschlagen, zwei Programmiertechniken nicht so unbedenklich zu mischen:
- Neue Objekte durch Konstruktoren und im Konstruktor definieren Methoden (d.h. immer Closures)
- Neue Objekte durch Kopien
Beide Techniken sind toll, aber siehst an diesem Beispiel, wie sie sich stören können. Ich würde hier bei einer Technik bleiben.
Erstmal: Keine Closures, alles über den Prototyp notieren:
function Charakter () {}
Charakter.prototype = {
stärke : 0,
geschick : 0,
vitalität : 0,
methode : function () {}
};
Immer mit this zum Zugriff auf die Instanz arbeiten.
Jetzt würde ich eine spezifische Klonfunktion schreiben, die new Character macht, alle Member der Instanz durchläuft und einfach herüberkopiert.
Das ist simple and stupid, aber funktioniert, solange du genau dieses Setup hast (Methoden sind bei allen Charaktern gleich, zu kopierende Eigenschaften sind Primitives, Charakter erbt keine weiteren Member).
Komplizieren könntest du die Sache machen, indem die Methoden nicht kopierst, denn sie existieren an der neuen Instanz schon. Ich weiß aber nicht, welchen Vorteil das bringt, schließlich ist es auch nach dem Überschreiben ein und dasselbe Funktionsobjekt (wg. Copy by Reference). Die Performance-Ersparnis ist wahrscheinlich durch typeof(member) != "function" wieder verloren.
Wenn du irgendwann Objekte (dazu zählen auch Arrays) anstatt Primitives kopieren möchtest, wird es schwieriger. Dann gehts nur so, wie ich anfangs gesagt hatte und worauf Christoph auch nochmal hingewiesen hatte: Funktion erstellen, das zu kopierende Objekt als Prototyp setzen und die Funktion instantiieren.
Mathias