Sinn und Unsinn von Closure
Closure kann, wie der Hersteller verspricht, vieles. Er macht JS-Code kompakter, kleiner und oft auch effizienter. Tools wie Closure gibt es fast schon so lang wie JavaScript selbst. Schon zur Netscape Zeit, als mit der damals vorherrschenden Layer Technologie erste aufwändige Scripte entstanden, wurde der Wunsch nach Optimierungen laut.
In der Tat ist das Optimieren von JavaScripts eine Sache für sich, da jeder JavaScript Interpreter (oder auch Browser) bestimmte Formulierungen schneller oder eben langsamer interpretieren könnte. Im Grunde genommen finden also fast alle Optimierungen auf logischer Ebene statt und lassen sich mit bewährten Regeln des Programmierens umschreiben:
- So lokal wie möglich, so global wie nötig - Anonyme Objekte und Referenzen durch das Verwenden von var vermeiden!
- Vorsicht bei Schleifen - Schleifen-Code Kompakt halten. Früh abbrechen. Vorsicht bei Verschachtelungen.
- Den Sinn und Zweck von Libs hinterfragen - jQuery, MooTools und andere machen das Leben einfacher. Doch ist die Verwendung von zwei, drei Funktionen es wert 100kb Quellcode zu übertragen und zu interpretieren?
- usw..
Daran wie gut oder schlecht ein JavaScript durchdacht ist kann Closure nichts ändern. Wohl aber an Dingen, die ein Programmierer zwar tun könnte aber praktisch nicht tut, weil es den Code unlesbar machen würde. Zum Beispiel bei Punkten wie
- Datenvolumen - Um so kleiner eine JS Datei ist um so schneller wird sie übertragen.
- Datenübertragung - Eine Datei ist sofort übertragen. Ein Script bestehend aus 30 Dateien erfordert 30 HTTP Requests an den Server.
- Konstante Daten - Konstanten könnten jedes Mal eingetragen, ggf. sogar Code, der dadurch nicht verwendet wird, gelöscht werden. Debug Konstanten z.B.
- Sauberer Code - Wie in meinem anderem Tutorial aufgezeigt, ist JavaScript sehr flexibel. Teils so flexibel, dass einzelne Browser Statements missinterpretieren, obwohl sie durch eine andere Schreibweise eindeutig wären.
Alle Dinge, die mit Fleiß oder Erfahrung machbar wären aber in der Praxis utopisch klingen. Gerade wenn es darum geht das Datenvolumen deutlich zu reduzieren oder als Konstante gedachte Variablen aufzulösen, ist es mit einem einfachen Replace-Script nicht mehr getan.
Und hiermit herzlich willkommen bei Closure

Installation und Anwendung
Closure lässt sich vielseitig verwenden. Der ursprüngliche Compiler ist in Java geschrieben und lässt sich als Binary-Jar File direkt von der Projekt-Homepage downloaden. Alternativ dazu gibt es zum schnellen Ausprobieren eine Online Applikation in der ohne Weiteres erste Schritte gemacht werden können. Ein Eclipse Plugin gab es wohl auch einmal ... da die URL von RockstarApps jedoch inzwischen nur noch auf Werbung verweist würde ich den IDE Usern unter euch dazu raten die Mühe für ein kurzes selbst gestricktes Batch oder Start-Script nicht zu scheuen. Praktisch jede IDE bringt alle nötigen Werkzeuge dafür mit.
Um mit dem Closure Compiler zu arbeiten, würde ich vorschlagen stets eine Art make.js anzulegen wo die einzelnen Source-Files sowie Einstellungen hinterlegt werden.
Code javascript:
1 2 3 4 5 6 7 | // ==ClosureCompiler==
// @output_file_name default.js
// @compilation_level SIMPLE_OPTIMIZATIONS
// @code_url ./meincode.js
// @code_url [url]http://tharo.tutorials.de/meincode.js[/url]
// ==/ClosureCompiler== |
Dieser Header kann auch in jedem File einzeln angelegt werden. Ich persönlich bevorzuge diese kombinierte Form des "Makefiles", da ich zum Einem wünsche, dass es nur einen zentralen Output gibt, zum Anderem zusätzliche Optionen über die Annotating-Statements weitere Unterscheidungen direkt an Ort und Stelle im Quellcode vornehmen will.
Generell ist es keine schlechte Idee sich durch die Wall-Of-Text der gesamten Closure Homepage durch zu wühlen. Aber gerade die eben angemerkte Annotating-Doku sollte man auf jeden Fall mal angelesen haben.
Beispiel
Wir Schnappen uns an dieser Stelle einmal meinen Code aus dem letzten Tutorial .
Code javascript:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | function Keks() {
this.geschmack = 'Himbeere';
this.essen = function() {
alert('jummi jummi');
};
}
SchokiKeks.prototype = new Keks;
function SchokiKeks() {
this.schlechtesGewissen = function() {
alert('Schoko-'+this.geschmack+', Hmmm ....');
return false;
};
} |
Code javascript:
1 2 3 | SchokiKeks.prototype.Share = function(mit) {
alert('Niemand teilt freiwillig mit '+mit+'!');
} |
Extern definieren wir noch die Methode "Share" für unseren Schokokeks. Im übrigen ein guter Weg für alle C++ puristen die ihre Methoden gerne außerhalb des Klassen Headers definieren wollen.
Für den ersten Versuch wählen wir die Einstellung SIMPLE_OPTIMIZATIONS.
Code javascript:
1 2 3 4 5 | function Keks(){this.geschmack="Himbeere";this.essen=function()
{alert("jummi jummi")}}SchokiKeks.prototype=new Keks;function SchokiKeks()
{this.schlechtesGewissen=function(){alert("Schoko-"+this.geschmack+",
Hmmm ....");return***}}SchokiKeks.prototype.Share=function(a){
alert("Niemand teilt freiwillig mit "+a+"!")}; |
Alle Zeilenumbrüche sind nachträglich zwecks Lesbarkeit eingefügt worden.
*** entspricht eigentlich !1 - wird aber wohl vom CODE Tag der Seite zerlegt.
Wir bemerken erst einmal, dass der Code zwar schon viel kompakter ist aber im Grunde sonst nicht viel passiert ist. Dies ändert sich erst mit der Einstellung ADVANCED_OPTIMIZATIONS.
JSC_USED_GLOBAL_THIS: dangerous use of the global this object at line 2 character 0
this.geschmack = 'Himbeere';
^
JSC_USED_GLOBAL_THIS: dangerous use of the global this object at line 4 character 0
this.essen = function() {
^
JSC_NOT_A_CONSTRUCTOR: cannot instantiate non-constructor at line 9 character 27
SchokiKeks.prototype = new Keks;
^
JSC_USED_GLOBAL_THIS: dangerous use of the global this object at line 11 character 0
this.schlechtesGewissen = function() {
^
JSC_USED_GLOBAL_THIS: dangerous use of the global this object at line 12 character 16
alert('Schoko-'+this.geschmack+', Hmmm ....');
this.geschmack = 'Himbeere';
^
JSC_USED_GLOBAL_THIS: dangerous use of the global this object at line 4 character 0
this.essen = function() {
^
JSC_NOT_A_CONSTRUCTOR: cannot instantiate non-constructor at line 9 character 27
SchokiKeks.prototype = new Keks;
^
JSC_USED_GLOBAL_THIS: dangerous use of the global this object at line 11 character 0
this.schlechtesGewissen = function() {
^
JSC_USED_GLOBAL_THIS: dangerous use of the global this object at line 12 character 16
alert('Schoko-'+this.geschmack+', Hmmm ....');
Obwohl mir nicht ganz klar ist, wieso Closure es nicht von selbst erkennt versehen wir unseren Code folglich mit einer Annotation die darauf hinweisen soll, dass es auch ganz wirklich um eine Klasse (in JavaScript: Construktor) geht.
Code javascript:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | /** @constructor */
function Keks() {
this.geschmack = 'Himbeere';
this.essen = function() {
alert('jummi jummi');
};
}
SchokiKeks.prototype = new Keks;
/** @constructor */
function SchokiKeks() {
this.schlechtesGewissen = function() {
alert('Schoko-'+this.geschmack+', Hmmm ....');
return false;
};
}
SchokiKeks.prototype.Share = function(mit) {
alert('Niemand teilt freiwillig '+this.geschmack+' mit '+mit+'!');
}
var test = new SchokiKeks();
test.schlechtesGewissen();
test.Share('BooBaer'); |
Das Resultat ist in diesem Fall deutlicher:
Code javascript:
1 2 3 | a.prototype=new function(){this.a="Himbeere"};function a()
{this.b=function(){alert("Schoko-"+this.a+", Hmmm ....")}}var b=
new a;b.b();alert("Niemand teilt freiwillig "+b.a+" mit BooBaer!"); |
- Der SchokiKeks ist als 'a' bezeichnet die einzige Klasse im Code.
- Der Keks selbst wird lediglich als anonyme Superklasse definiert.
- 'schlechtesGewissen' liefert keinen Wert mehr zurück, da selbiger nie verwendet wird.
- Die 'Share' Methode, obwohl sie das selbe Zugriffsmuster hat wie 'schlechtesGewissen' wird aufgrund ihrer Definition vollständig aus der Klasse ausgehängt und anonymisiert.
- Alle Bezeichner wurden zu Gunsten der Codelänge umbenannt.
Hätten wir im Beispiel die Methodenaufrufe in den letzten Zeilen unterlassen, wäre der Code weiter, bis hin auf 0 Zeilen zusammen gekürzt worden.
Fazit
PRO: Die Optimierungen, die durch den Einsatz des Closure Compilers erzielt werden können ist beachtlich. Nicht nur, dass es oft interessant ist zu sehen wie Code optimiert wird, es verspricht sich zumindest in der Advanced-Einstellung ein echtes Plus. Arbeitet man von Anfang an konsequent mit dem Compiler, genießt man den Luxus nicht mehr zwischen OOP einwandfreien und performanten Code wählen zu müssen.
CONTRA: Der Compiler braucht bei seiner Arbeit durchaus Hilfestellung. Um sein volles Potential entfalten zu können, müssen Konstanten, Klassen und ähnliche Konstrukte im Quellcode zusätzlich markiert werden. All der Aufwand lohnt sich erst bei Projekten ab einer gewissen Größe. Eine nachträgliche Anwendung des Compilers auf bestehenden Code kann schnell zu unerwarteten Problemchen führen.
So. Ich hoffe damit den Closure Compiler halbwegs treffend vorgestellt zu haben. Ich freue mich auf euer Feedback und viel Spaß beim Ausprobieren!
-tharo




Bereiche
Kategorien
Forum - Webmaster & Internet





tutorials.de-Systemmitteilung