Vererbung in Javascript anhand von Exceptions
Hab noch zwei weitere Artikel gefunden, beide englisch:
- http://www.ruzee.com/blog/2008/12/javascript-inheritance-via-prototypes-and-closures
- http://ejohn.org/blog/simple-javascript-inheritance/
Jop, ein weiterer Artikel zu Javascript. Und zur "komischen" Vererbung von Javascript.
Jeder Programmierer kennt Exceptions, diese netten Fehler die einen an den Kopf geschmissen werden, wenn was schief läuft. Auch sowas gibts bei Javascript:
try {
doSomething();
} catch(e) {
alert(e.toString());
}
Klassisches try/catch-Statement. Demnächst muss ich nochmal nachschauen wie man "verschiedene" Exceptions erkennt und dann getrennt behandelt. Nunja, weiter gehts, wir bauen uns eine Exception:
throw 42;
throw "I'm a string";
throw {toString: function() { return "I'm an object!"; } };
Alles wunderbar, aber String-Nachrichten schmeissen kann ja jeder. Kommen wir also zu der "BaseException":
function BaseException(_message) {
this.name = "BaseException";
this.message = _message;
}
BaseException.prototype = {
name : null,
message : null,
toString : function() {
return this.name + ': "' + this.message + '"';
}
}
Erstmal was ganz simples. Kann man z.B. so schmeissen und fangen:
try {
throw new BaseException("foobar");
} catch (e) {
alert(e.toString()); // alerts: "BaseException: foobar"
}
Schön, nicht wahr? Jetzt brauchen wir noch Vererbung:
// ripped from http://www.sitepoint.com/blogs/2006/01/17/javascript-inheritance/
function extend(descendant, parent) {
var sConstructor = parent.toString();
var aMatch = sConstructor.match(/\s*function (.*)\(/);
if (aMatch != null) {
descendant.prototype[aMatch[1]] = parent;
}
for (var m in parent.prototype) {
descendant.prototype[m] = parent.prototype[m];
}
};
Dies ist wohl leider notwendig. Diese "extend"-Function macht das irgendwie wohl cross-browser-kompatibel. Nunja, für Mozilla-only geht es auch anders. Nunja, "geerbt" wird jetzt so:
function OtherException(_message) {
// call "super" constructor
this.BaseException(_message);
this.name = "OtherException";
}
// OtherException inherits from BaseException
extend(OtherException, BaseException);
Voilà. Fertig ist das Butterbrot:
try {
throw new BaseException("foobar");
} catch (e) {
alert(e.toString()); // alerts: "BaseException: foobar"
}
try {
throw new OtherException("foobar");
} catch (e) {
alert(e.toString()); // alerts: "OtherException: foobar"
}
Eclipse, Subversion und Spket
Die drei Sachen verwende ich gerade. Da aber irgendwie gerade was abschmiert, steige ich mal auf "unstable" (aka 3.5M3)um:
http://download.eclipse.org/eclipse/downloads/
Eclipse Update URLs:
- Subclipse: http://subclipse.tigris.org/update_1.4.x
- Spket IDE: http://www.spket.com/update
- Subversive
- http://down...rsive/0.7/update-site/ - [required] Subversive plug-in
- http://www.polarion...load/eclipse/2.0/update-site/ - [required] Subversive SVN Connectors
- http://www.polarion...wnload/integrations/update-site/ - [optional] Subversive Integrations
public, private und “privileged” in Javascript
Jetzt wirds hässlich. Natürlich gibts "public" und "private" auch in Javascript. Allerdings, wie immer, ein bisschen anders:
Public
function Constructor(value) {
this.membername = value;
}
Constructor.prototype.membername = value;
myConst = new Constructor("foobar");
print myConst.membername; // prints 'foobar'
Private
function Constructor(value) {
var self = this;
this.membername1 = value; // public member
var membername2 = value + "private"; // private member
function membername() { // private method
print self.member; //access to public member in private method
print membername2; // access to private member in private method
}
}
myConst = new Constructor("foobar");
print myConst.membername1; // prints 'foobar'
print myConst.membername2; // prints 'undefined'
myConst.membername(); //no such function
Wenn man jetzt aber z.B. mittels dem property "prototype" auf private-members zugreifen will, dann wird man scheitern. "public" hat keinen Zugriff auf "private". Dazu muss man die "privileged"-Methoden verwenden.
Privileged
function Constructor(value) {
this.membername = function membername() {
print "hi";
};
}
myConst = new Constructor("foobar");
myConst.membername(); // prints "hi"
Javascript-Klassen und Objekte
Habe gerade noch das hier gefunden: Class-Based vs. Prototype-Based Languages
Ich hab zwar schon hier auf einen schönen Artikel verwiesen, aber jetzt nochmal das ganze aus meinem Munde.
Javascript is Objektorientiert. Schon immer. Glaub ich. 1995 das erstmal mit Netscape veröffentlicht, sind wir inzwischen bei Javascript 1.8 angekommen (seit 18. Dezember 2007). Javascript wird übrigens von Mozilla "gemanaged". Und Javascript ist nicht Ecmascript. Vielmehr ist Javascript ein Dialekt von Ecmascript.
Soviel dazu. Javascript implementiert ECMAScript, genauer ECMA-262, edition 3. Innerhalb davon gibt es keine "Klassen" im eigentlichen Sinne. Viel eher werden direkt Objekte geboren.
function meinObjekt () {
this.zahl = 1;
}
Und schon haben wir ein Objekt/Klasse mit dem Namen "meinObjekt". Und eine Eigenschaft "zahl" hat es auch.
var objekt = new meinObjekt();
window.alert(objekt.zahl);
Und somit kriegen wir ein Fenster mit einer "1" drinnen. Es gibt auch private und öffentliche Eigenschaften/Methoden, allerdings ist Javascript da etwas bockig:
function meinObjekt () {
var self = this;
/** @private */
var foobar = "private";
var privateMethod = function () {
window.alert(foobar + " " + self.barfoo);
};
/** @public */
this.barfoo = "public";
this.public_method = function () {
window.alert(foobar + " " + this.barfoo);
private_method();
};
}
Wer sich wundert was das "self" soll, das liegt an Javascript. Während der Zugriff von öffentlichen auf private Methoden prima klappt, ist der Zugriff von privaten auf öffentliche Methoden ... anders
. Hierzu wird einfach "this" in "self" kopiert und dann kann wie "this" verwendet werden. Jippie.
Ich verwende den Spass gerade so:
var logger = function() {
};
logger.prototype = {
log : function(msg) {
console.log(this.getNow() + msg);
},
getNow : function() {
return new Date();
}
};
var foobar = new logger();
logger.log("foobar"); // prints "foobar"
Man spricht hier von "prototyp-basierter" Objektorientierung. Interessant wirds in dem "neuen" Standard ECMA-262, edition 4. Dort gibt es dann tatsächlich auch Klassen:
class C {
var val // a variable property
var large = Infinity // a variable property
const x = 3.14 // a constant property
function f(n) { return n+val*2 } // a method property
}
Da ja Javascript schön dynamisch ist, gibt es auch dynamische Klassen:
dynamic class C {
}
c = new C
c.x = 37 // adds a property
delete c.x // removes it again
Weiter gehts mit Vererbung, jetzt mit Schlüsselwörtern wie "extends":
class C {
var val
function f(n) { return n+1 }
}
class BetterC extends C {
var large = 123456 // new value property
override function f(n) { return n*3 } // overridden method property
}
Es gibt auch "final" als Schlüsselwort, eine so bezeichnete Klasse kann dann keine Kinder kriegen. Und zu guter Letzt werden auch Interfaces eingeführt. Somit wird Ecmascript und somit auch Javascript an die "normale" Objektorientierung herangeführt. Die komplette Spezifikation des "neuen" ECMAScripts findet sich unter:
Test und so
hab heute mein eigenes Unit-Test-Dings geschrieben. Kann nix, bin aber stolz wie Oskar
Javascript aus FF-Erweiterung in der Webseite benutzen
Langer Titel, kurzer Blogeintrag. Ich bastel gerade daran rum, diverse Javascript-Funktionen innerhalb der HTML-Seite zu benutzen. Das kann ziemlich hässlich werden:
var head = this.getContentDocument().getElementsByTagName("head")[0];
var script = this.getContentDocument().createElement("script");
var showImage = this.getContentDocument().createTextNode("function showImage(id) { "
+ "var image = document.getElementById(id);"
+ "image.style.display = \"inline\"; "
+ "image.parentNode.previousSibling.addEventListener(\"mouseout\",
function() {"
+ "window.setTimeout(fadeImage, 1500, id);"
+ "}, false)"
+ "}");
script.appendChild(showImage);
head.appendChild(script);
Alles doppelt escapen und so. Kein Spass. Hässlich zum Lesen. Lösung:
var head = this.getContentDocument().getElementsByTagName("head")[0];
var script = this.getContentDocument().createElement("script");
script.setAttribute("src", "resource://jslibs/foobar.js");
script.setAttribute("type", "text/javascript");
head.appendChild(script);
Nix spektakuläres, aber innerhalb der Datei "foobar.js" kann man jetzt 'ganz normales' Javascript schreiben. Ich freu mich über sowas
Registry Pattern in Javascript
Wer globale Objekte vermeiden will, aber trotzdem globalen Zugriff will (z.B. auf eine Datenbankinstanz) baut sich eine Registry. Im Prinzip nur das Singleton-Pattern mit get und set-Methoden:
registry = (function() {
// private attributes
var blubb = new Array();
// private methods
var getIndex = function(_name) {
for (var i = 0; i < blubb.length; i++) {
if (blubb[i].name == _name) {
return i;
}
}
return null;
};
return {
// public attributes
// public methods
setValue : function(_name, _value) {
var foo = this.getValue(_name);
if (foo == null) {
var temp = new Object();
temp.name = _name;
temp.value = _value;
blubb.push(temp);
} else {
blubb[getIndex(_name)].value = _value;
}
},
getValue : function(_name) {
for (var i = 0; i < blubb.length; i++) {
if (blubb[i].name == _name) {
return blubb[i].value;
}
}
return null;
}
}
})();
// registry is already available
registry.setValue("foo", "bar");
registry.getValue("foo"); // gives "bar"
registry.getValue("bar"); // gives null
Siehe auch: http://yuiblog.com/blog/2007/06/12/module-pattern/
Javascript-Objekte
Guter Artikel über Objekte in Javascript. Eher nur Grundsätzliches, trotzdem lesenswert: JavaScript's class-less objects im JavaRanch Journal.
