JavaScript ist eine interessante Sprache. Sie hat sich jahrelang mit ihrem Ruf als das häßliche kleine Browser-Scripting-Ding zufriedengegeben, mit dem man sich als Webentwickler eben herumschlagen muß. Seit relativ kurzer Zeit wandelt sich jedoch sowohl das Selbstverständnis der JavaScript-Kultur als auch die Sprache selbst. JavaScript wird nicht mehr nur verwendet, sondern von einigen noch Wenigen auch geliebt. Es entwickelt sich langsam eine Gefolgschaft wie sie bei Python, Ruby und Lisp existiert und die die weitere Entwicklung der Sprache lenkt.

Sie ist es, die versucht, JavaScript aus einer Skriptsprache für Webbrowser in eine auch anderweitig eingesetzte Sprache zu verwandeln. Solche Dinge wie ein (mit den bisherigen Konzepten integriertes) Klassensystem und bessere Unterstützung für lexikalische Variablenbindung und Metaprogrammierung soll die Sprache mit ECMAScript Harmony bekommen.

Das für ECMAScript Harmony vorgeschlagene Metaobjektprotokoll erscheint dabei zunächst einmal unspektakulär. Sicher, es wird möglich sein, Zugriffe auf Attribute abzufangen. Man kann Aufzählungen mit for in umprogrammieren. Man kann Funktionen simulieren. Das sind aber allesamt Dinge, die auch zum Beispiel in Python leicht sind.

Es gibt allerdings einen kleinen, feinen Unterschied zu dem, was Python mit den Unterstrich-Methoden bietet*: Die Objekte (in diesem Kontext Proxies genannt), deren Verhalten umprogrammiert wird, tun das nicht selbst. Stattdessen ist dafür ein sogenanntes Handler-Objekt zuständig. Auf alles, was mit dem Proxy passiert, kann das Handler-Objekt reagieren. Attributszugriffe, -veränderungen und -löschungen, Aufzählungen, das Ändern von Eigenschaften von Attributen, Methodenaufrufe — das Verhalten des Proxys wird vollständig durch das Handler-Objekt implementiert.

Das ist kein unbekanntes Konzept: Es ist das Konzept des Metaobjekts. Grundsätzlich ist ein Metaobjekt ein Objekt, das das Verhalten anderer Objekte beschreibt. (Dabei wird es natürlich seinerseits unter Umständen von einem Metaobjekt beschrieben, welches auch es selbst sein kann.) Offenbar ist ein Handler-Objekt genau das. Sehen wir uns das API einmal an: Es gibt im vorgeschlagenen Metaobjektprotokoll die Methode get, die für jeden Attributslesezugriff aufgerufen wird und das Ergebnis berechnet. Das erinnert sehr an — richtig — AMOP, das de-facto-Standardmetaobjektprotokoll für Common Lisp, nämlich konkret an dessen Methode slot-value-using-class: Genau wie get in JavaScript ist slot-value-using-class nicht polymorph im konkreten Ziel-, sondern dessen Metaobjekt (in diesem Fall der Klasse). Gleiches gilt für alle anderen Methoden im Harmony-Metaobjektprotokoll.

Entsprechung von AMOP- mit JavaScript-Handler-Methoden für den Attributzugriff
delete slot-makunbound-using-class
get slot-value-using-class, compute-effective-method
set (setf slot-value-using-class)
has slot-boundp-using-class

Natürlich bricht die Analogie spätestens an dem Punkt zusammen, an dem es einerseits in Common Lisp um Klassen und generic functions geht, die es in JavaScript nicht gibt — und andererseits bei der generischen Aufzählung von Containerobjekten, die Common Lisp nicht bietet. Dennoch: Die Nähe ist vorhanden, und, jedenfalls gefühlt, gegenüber Lisp stärker als gegenüber Python, Ruby oder gar Java.

Eine Einführung in das Metaobjektsystem von JavaScript (welche möglicherweise auch hilft, die Ideen hinter AMOP zu verstehen) gibt der Vortrag „Changes to ECMAScript, Part 2: Harmony Highlights — Proxies and Traits“ von Tom Van Cutsem.


(*) Mit Deskriptoren gehen — zumindest für Objekte, die nicht selbst Klassen sind — ähnliche Dinge. Attributszugriffe folgen in Python grundsätzlich recht komplizierten Regeln, und philosophisch finde ich die Entfernung zu JavaScripts Proxy-Objekten ziemlich fühlbar.