Javascript: Einsatz des new-Operators mit Variablen und dynamischer Vorgabe der Konstruktorfunktion

In PHP gibt es die Möglichkeit, den “new” Operator mit einer Variablen zu kombinieren. Dies entspricht einer indirekten Adressierung des Konstruktors. Beispiel :

$ClassName = "Class_Test"; 
$Obj = new $ClassName( param1, param2, ...); 

 
Probiert man etwas Ähnliches unter Javascript, so erleidet man Schiffbruch. Der String-Wert einer Variablen wird vom “new” Operator nicht als Bezeichnung einer Konstruktorfunktion interpretiert.

Sieht man typische Webseiten oder auch Bücher nach der Verwendung des “new”-Operators durch, so wird nach dem “new”-Operator fast durchgehend die Benutzung eines Literals beschrieben, das dem Namen der definierten Funktion entspricht:

function meterpreter() {
	this.alpha = 5; 
}      
var Obj = new meterpreter(); 
echo "value of property alpha = " +  Obj.alpha;    

 
Die Verwendung eines Literals scheint eine indirekte, dynamische Adressierung auszuschließen.

Nun passiert es einem aber auch unter Javascript, dass man ein Objekt gerne aus einem dynamischen Kontext heraus erzeugen möchte. Dann will man den “new”-Operator mit einer Variablen kombinieren, die dynamisch ermittelt wurde, und deren Wert irgendwie die Konstruktorfunktion referenzieren soll. Ein Beispiel:

Man möchte beim Startup einer Webseite eine Reihe von (Singleton-) Objekten erzeugen. Die Namen der zugehörigen
Konstruktorfunktionen seien in einem Array gespeichert. Ein Loop über das Array könnte die Objekterzeugung durchführen, wenn der “new”-Operator mit dem jeweiligen Array-Eintrags verbunden werden könnte und dadurch dann indirekt auch der jeweils benötigte der Konstruktor aufgerufen werden würde. Wie also nutzt man den “new”-Operator zusammen mit Variablen unter JS?

Hier hilft es, sich daran zu erinnern, dass definierte JS-Funktionen Objekten entsprechen, die sich Variablen zuordnen lassen. Nehmen wir in Anlehnung an das obere Beispiel an, wir hätten ein globales Objekt definiert, dass wir GOC (Global Object Controller) nennen wollen und das ein Array mit Namen für potentielle (Singleton-) Objekte beinhaltet. Nehmen wir ferner an, dass wir zugehörige Konstruktorfunktionen bestimmten Variablen des GOC mit normierten Namen zugeordnet hätten:

this.GOC = new Global_Object_Controller();  
this.GOC.createObjects(); // hier konstruieren wie Objekte als Elemente des GOC

function Global_Object_Controller() {
	this.ay_ctrl_obj_names  = new Array('Gallery', 'Ajax', ....);
	this.num_ctrl_objs = this.ay_obj_names.length;

	// Konstruktor Definition
	// ----------------------- 
	this.Constr_Gallery = function( param ) {
	 	.....	
		this.alpha = param + 1; 
		.....
		.....
 	};

	// Method definition of Constr_Gallery 
	// Wir verwenden zur Abwechslung mal die an Arrays angelehnte Notation ...
	this['Constr_Gallery'].prototype.do_something = function() {
		...
		this.obj_num += 10; 
		...
	};  
	.....
	..... 	

	// Konstruktor Definition
	// ----------------------- 
	this.Constr_Ajax = function( param ) {
	 	.....	
		this.obj_num = param + 1; 
		.....
		.....
 	};
	....
	....
}

 
Dann können wir z.B. über eine Methode tatsächlich Objekte dynamisch erzeugen lassen – und zwar z.B. über folgenden simplen Trick:

Global_Object_Controller.prototype.createObjects = function() {
	
	var ctrl_obj_name = ''; 
	var constr_name = ''; 
	....
	for (i=0; i<this.num_ctrl_objs; 
i++) {
		...
		ctrl_obj_name = "Obj_" + this.ay_ctrl_obj_names[i]; 
 		constr_name   = "Constr_" + this.ay_ctrl_obj_names[i];

		// Indirekter Aufruf des Konstruktors über eine Variable (hier des GOC)  
		// *******************************************************************
		this[ctrl_obj_name]  = new this[constr_name](i); 
		// ******************************************************************** 		
		// ist identisch mit 
		// window.GOC[ctrl_obj_name] = new window.GOC[constr_name](i); 		
	
		// console.log("Objekt " + ctrl_obj_name + " im GOC kreiert");  
  		....
	}	
	.....
	// console.log("Obj_Gallery.obj_num = " + this["Obj_Gallery"].obj_num ); // value=1 
	this["Obj_Gallery"].do_something(); 
	// console.log("Obj_Gallery.obj_num = " + this["Obj_Gallery"].obj_num ); // value=11  
	// console.log("Obj_Ajax.obj_num = "    + this["Obj_Ajax"].obj_num );    // value=2  
	
	.....
};   

 
Das funktioniert! Man erkennt, dass in unserem sehr simplen Beispiel die gewünschten Objekte über Variablenaufrufe als Elemente des GOC-Objekts erzeugt werden.

Man kann den “new”-Operator also tatsächlich mit Variablen (irgendwelcher Objekte oder von “window”) kombinieren, wenn diesen Variablen bereits sinnvolle Konstruktorfunktionen zugeordnet wurden. Das erinnert an den Callback-Mechanismus – hier allerdings für den “new”-Operator.

Die indirekte Adressierung erfolgt im Gegensatz zu PHP dadurch, dass man den “Namen” der Variablen im Zuge des Algorithmus dynamisch festlegt und darüber dann auf den Konstruktor als das Funktions-Objekt referenziert. Das erfordert zwar ein etwas anderes Vorgehen, schränkt einen aber im Vergleich zu PHP in der Zielerreichung einer dynamischen und indirekten Konstruktor-Referenzierung nicht ein.

Dadurch wird man bzgl. der Objektgenerierung sehr viel flexibler, als wenn man sich immer mit Literalen herumschlagen müsste.

Off Topic:

Das Beispiel zeigt übrigens, dass Konstruktorfunktionen nicht immer zwingend im globalen Kontext (window) platziert werden müssen. Sie können auch jederzeit Variablen eines bereits definierten übergeordneten Objekts (oder seines Konstruktors) zugeordnet werden. Dies eröffnet in bestimmten Fällen weitere Möglichkeiten der Kapselung von Code.
In der Praxis muss man sich eine Zuordnung von Konstruktor-Funktionen zu Variablen aber gut überlegen. Schon aus Gründen der Codeverwaltung wird man Konstruktorfunktionen für wichtige Objekt in separaten, eigenen Dateien aufbewahren. Beim Laden der Dateien werden die Funktionen dann typischerweise in den globalen Kontext platziert.
Zudem gilt: In komplexen Codes, die z.B. mit Eclipse oder anderen Tools gewartet werden, ist die Zuordnung von Konstruktorfunktionen zu Variablen eines anderen Konstruktors möglicherweise mit dem Nachteil verbunden, dass Code-Outliner diese Sub-Konstruktoren, zugehörige Variable und Funktionen ggf. nicht mehr als eigene Objekte ausweisen – sie sind dann einer einfachen Navigation im Code über den Outliner nicht mehr zugänglich.

Will man die Konstruktoren deshalb aus guten Gründen unabhängig von anderen Objektbeschreibungen definieren, so ist das Codebeispiel leicht zu modifizieren: Man definiert die Konstruktorfunktionen global und nicht im GOC. Und ruft sie dann im GOC und dessen Loop wie folgt auf:

this[ctrl_obj_name] = new window[constr_name](i);

Viel Spaß weiterhin mit Javascript!

Hilfreiches Eclipse Plugin: wtp-webresources

Ich versuche ja gerade, meine Frau dazu zu bringen, statt des proprietären und nicht linux-tauglichen und wenig stabilen Dreamweavers die IDE Elipse für HTML und CSS Coding zu benutzen. Zur Auswahl stehen dort die Web Standard Tools (WST) oder z.B. das Aptana Studio Plugin. Nun zieht die Installation des Aptana Plugins immer noch einige unangenehme Nebeneffekte nach sich, so dass man sich lieber mit den Editoren der nativen Eclipse Web Developer Tools begnügen möchte.

Ein Nachteil des WST Eclipse HTML Editors für Eclipse-Umsteiger ist u.a. folgender:

Man erhält zwar Code Assist Vorschläge bzgl. elementarer CSS2- oder CSS3-Anweisungen für Inline-Styles. Beim Editieren des “class”-Attributs eines HTML-Tags erhält man jedoch keine Assist-Angebote zu bereits definierten Style-Anweisungen für Classes, die in CSS-Dateien eines Eclipse-Projekts enthalten sind – auch dann nicht, wenn man solche CSS-Dateien bereits explizit im HEAD-Bereich der HTML-Datei eingebunden hat. Umgekehrt erhält man bei einem Hover über die Klassendeklaration eines HTML-Tags keine Informationen zu den zugehörigen Style-Festlegungen.

Ähnliche Defizite gibt es im Bereich der Ergänzung von Bild-, CSS-File- und JS-File-Ressourcen. Hier ließen sich Content Assist Angebote ja prinzipiell über die Inhalte der Projektverzeichnisse zusammenstellen.

Das Plugin “wtp-webresources” behebt all diese Schwächen und ist damit ein wirklich nützliches, empfehlenswertes Helferlein für Eclipse Mars. Siehe:
https://github.com/angelozerr/eclipse-wtp-webresources

Ob das meine Frau überzeugen wird, weiß ich noch nicht – der nächste Punkt, den sie im Vergleich mit Dreamweaver mit Sicherheit vermissen wird, ist eine Darstellung der effektiv wirksamen CSS-Styles für ein HTML-Tag aus der Kombination der für das Tag festgelegten “id”-bezogenen, “class”-bezogenen Styles, aktuellen Inline-Styles und zugehöriger Spezifizitätsregeln. Wäre auch in Eclipse nützlich ….

Opensuse Leap 42.1 und 13.2 – aktuelle Versionen des freshplayerplugin-Pakets führen zum Crash von Eclipse mit GTK2

Es gibt Probleme, deren Ursachen sind schwer zu finden und können einen in den Wahnsinn treiben. So zuletzt das Phänomen, dass Eclipse mit GTK2 nach Updates sowohl von Opensuse 13.2 als auch Opensuse Leap 42.1 permanent und zunächst scheinbar erratisch abstürzten.

Interessanterweise tauchen die Probleme mit Eclipse unter GTK3 nicht auf. Allerdings zeigt die Eclipse UI unter GTK3 noch einige unschöne Grafik-Probleme.

Es dauerte eine Weile, bis ich herausfand, dass die Abstürze von Eclipse/GTK2 nur auftraten, wenn ich Content Assist Funktionalitäten benutzte (was ich natürlich sehr regelmäßig tue) und dabei Zusatzinformationen zu Elementen der Vorschlagsliste angezeigt wurden. Typischerweise ist es so, dass diese Zusatzinformationen aus HTML-Vorgaben gerendert werden. Die Java Runtime JVM nutzt hierzu “webkit”-Funktionalitäten (der Umgebung).

Es dauerte noch einige Zeit mehr, bis ich herausfand, dass das Problem auf einer frischen Installation von OS 13.2 oder LEAP 42.1 nicht auftrat – wohl aber nach einer Reihe von Updates der Opensuse-Umgebung. Leider ist es durchaus schwierig, herauszufinden, welches Paket unter hunderten genau der Verursacher der Probleme ist. Dass es mit Web/HTML-Rendering zu tun haben musste, war aber klar.

Durch eine Backtrace-Analyse des JVM-Absturzes kam ich heute einen substanziellen Schritt weiter:

Die erhaltenen Absturz-Informationen der JVM zeigten, dass im Rendervorgang auch eine “freshwrapper”-Bibliothek tangiert wurde – genauer:
“/usr/lib64/browser-plugins/libfreshwrapper-pepperflash.so” aus dem Paket freshplayerplugin.

Das Paket “freshplayerplugin” liefert einen nützlichen Firefox-Wrapper für Chromes/Chromiums “pepper-flash”-Plugin. Letzteres ermöglicht das Abspielen von Flash-Inhalten auch unter Firefox – das ist u.a. nützlich für Live-Streams des einen oder anderen Fernsehsenders, der bislang noch nicht auf vernünftige Formate umgestellt hat.

Ich hatte das Paket “freshplayerplugin” unter Leap 42.1 und OS 13.2 im Zuge von Updates in der aktuellsten Variante “0.3.4-20.1” vom Packman-Repository installiert.

Tatsächlich zeigte sich, dass mit einer Installation der Paketversion “0.3.2” aus den jeweiligen Hauptrepositories von SuSE die Eclipse-Absturz-Probleme unter Opensuse Leap 42.1 verschwanden. Bzgl. OS 13.2 muss ich mich noch kundig machen.

Ich hoffe, diese Info zumindest zu Leap 42.1 hilft auch anderen Betroffenen, die Eclipse mit GTK2 nutzen wollen.