molily Navigation

Objektorientiertes JavaScript mit YUI 3

Nachdem ich die Grundlagen von OOP in JavaScript geschildert habe, möchte ich mich Frameworks und deren OOP-Konzepten zuwenden. Die dort beschriebenen Sachverhalte werden hier weitesgehend als bekannt vorausgesetzt, vor allem das Wissen über Konstruktoren, Prototypen und Instanzen.

Pseudoklassen in JavaScript

Viele JavaScript-Frameworks enthalten Hilfsmittel, um komplexe Vererbungshierarchien aufzubauen. Sie nutzen die Prototypen-Ketten, um einem Objekt mehrere Prototypen zuzuweisen. Diese Helferbibliotheken erlauben die Deklaration von Pseudoklassen, um Programmierern, die klassenbasierte OOP kennen, den Einstieg zu vereinfachen. Beispiele sind Prototype (Class.create), Mootools (new Class), JS.Class (new JS.Class) und Joose (Class).

JavaScript kennt keine nativen Klassen. Ich bin mehr und mehr zu der Ansicht gekommen, dass diese Analogie das profunde Verständnis von JavaScript eher erschwert als vereinfacht. Natürlich kochen die Pseudoklassen aus Prototype, Mootools & Co. auch nur mit (klassenlosem) JavaScript. Zur gegebenen Zeit möchte ich deren interne Funktionsweise erklären, aber zunächst möchte ich mich YUI widmen.

YUI 3 – Yahoo! User Interface Library

Das Framework YUI 3 verfolgt einen etwas anderen Ansatz. Es verbirgt die tatsächliche Funktionsweise nicht hinter Abstraktionen wie Pseudoklassen und fühlt sich daher mehr wie JavaScript an. YUI bringt nichtsdestoweniger brauchbare Hilfsmittel für Mehrfachvererbung mit sich. Anstelle von Pseudoklassen-Deklarationen bietet YUI verschiedene Helferfunktionen. Diese suggerieren nicht, das »Klassen« erzeugt werden, sondern versuchen der funktionalen, prototypischen und dynamischen Natur von JavaScript Rechnung zu tragen.

YUI arbeitet stark gekapselt, asynchron und modularisiert. Die hier vorgestellten Techniken sind entweder Teil des Kerns oder des oop-Moduls. Der hier gezeigte Code muss zumindest in folgenden Aufruf gekapselt werden:

<script type="text/javascript"
  src="http://yui.yahooapis.com/3.1.1/build/yui/yui-min.js"></script>
<script type="text/javascript">
YUI().use('oop', function (Y) {
    /* Der Code kommt hierhin. */
});
</script>

Zunächst wird der sogenannte YUI Seed eingebunden, mithilfe dessen weitere YUI-Module geladen werden können. Über YUI().use('oop') geben wir Code an, der auf dem oop-Modul aufbaut. YUI lädt automatisch die benötigten JavaScript-Dateien, erzeugt ein Objekt mit den gewünschten Modulen und übergibt es an die Callback-Funktion. Darin ist das Objekt unter dem Namen Y verfügbar.

Das oop-Modul wird von fast allen gängigen YUI-Modulen, darunter dom verwendet. Wenn Sie YUI für die üblichen Zwecke des DOM-Scripting verwenden, dann wird oop in der Regel als abhängiges Modul bereits eingebunden.

  1. Y.Object – Einfache prototypische Vererbung ohne (sichtbare) Konstruktoren
  2. Y.extend – Konstruktoren und mehrfache prototypische Vererbung
    1. Interne Funktionsweise von Y.extend
    2. Weitere Parameter von Y.extend: Kompaktschreibweise und statische Eigenschaften
  3. Mixins mit Y.augment
    1. Einzelnen Objekten Funktionalität hinzufügen
    2. Weitere Parameter von Y.augment: Überschreiben erzwingen und Whitelist
  4. Eigenschaften kopieren mit Y.mix
    1. Weitere Parameter von Y.mix: Überschreiben erzwingen und Whitelist
    2. Mehrere Objekte vereinen mit Y.merge
    3. Y.augment vs. Y.mix
  5. Links und Quellen

Y.Object – Einfache prototypische Vererbung ohne (sichtbare) Konstruktoren

Da JavaScript-OOP nicht auf Klassen, sondern Prototypen basiert, ist die Grundidee, dass gewöhnliche Objekte von gewöhnlichen Objekten abgeleitet werden. Leider kann die Prototypen-Ketten nicht browserübergreifend direkt manipuliert werden, sondern es ist der Umweg über Konstruktoren, deren prototype-Eigenschaft sowie die Instantiierung mit new nötig. Dies ist aus klassenbasierten Sprachen wie Java bekannt – in JavaScript aber oftmals schwerfällig und fehl am Platze.

Die Helferfunktion Y.Object ermöglicht direkte prototypische Vererbung. Sie erzeugt ein neues Objekt, welches ein anderes als Prototyp besitzt. Y.Object nimmt uns den Umweg über eine Konstruktorfunktion ab. Intern wird durchaus mit einem Konstruktor, seiner prototype-Eigenschaft und new gearbeitet, allerdings wird mit der Funktion nur eine Instanz erzeugt und sie wird daraufhin weggeworfen. So sieht der Quellcode von Y.Object aus:

Y.Object = function(o) {
    var F = function() {};
    F.prototype = o;
    return new F();
};

(An dem Funktionsobjekt Y.Object hängen übrigens noch weitere Funktionen – wenn hier von Y.Object die Rede ist, dann immer von der obigen Funktion, nicht deren Kindmethoden.)

Dieses Pattern hat Douglas Crockford »erfunden«, der nicht zufällig als JavaScript-Architekt bei Yahoo angestellt ist. Diese Funktion ist als Object.create bekannt und gehört seit ECMAScript 5 zum Standardrepertoire von JavaScript.

Die Anwendung gestaltet sich sehr einfach. Man erzeugt einfache Objekte mit dem Objekt-Literal und leitet mit Y.Object neue Objekte von diesen ab. Diese wiederum können modifiziert und erweitert werden:

var bird = {
    flighted: true,
    noise: "Chirp, chirp!",
    sing: function () {
        alert(this.noise);
    }
};

var robin = Y.Object(bird);
robin.sing();

var chicken = Y.Object(bird);
chicken.flighted = false;
chicken.noise  = "Cluck, cluck!";

var ginger = Y.Object(chicken);
ginger.name = "Ginger";
var rocky = Y.Object(chicken);
rocky.name = "Rocky";

Das Beispiel erzeugt ein bird-Objekt, einen Vogel. Dieser wird als flugfähig angenommen (flighted) und kann Laute von sich geben (Methode sing).

Davon abgeleitet werden das robin- sowie das chicken-Objekt. Das chicken-Objekt bekommt etwas abweichende Eigenschaften, denn das Huhn kann nicht fliegen und gackert, anstatt zu zwitschern. Schließlich werden zwei weitere Objekte angelegt, die von chicken erben.

Immer wenn ein Objekt mit der Funktionalität von bird benötigt wird, wird mittels Y.Object ein neues Objekt erzeugt, welches bird als Prototypen besitzt. Wird davon ein weiteres Objekt abgeleitet, besitzt es zwei Prototypen (ginger hat die Prototypen chicken und bird).

Für Programmierer, die gedanklich in klassenbasierter OOP verhaftet sind, mag das ein Albtraum sein: Es gibt keine Trennung zwischen abstrakten Typdeklarationen einerseits und Instanzen andererseits, sondern bloß Objekte und Prototypen. Wenn man jedoch einmal prototypische Vererbung verstanden hat, so erscheint mir obiges Pattern einfacher und logischer, als in jedem Fall ausdrücklich mit Konstruktorfunktionen, prototype und new zu arbeiten. Object.create bzw. Y.Object ermöglichen die Abstraktion von dieser internen Umsetzung.

Y.extend – Konstruktoren und mehrfache prototypische Vererbung

Neben dem radikalen prototypischen Ansatz von Y.Object, der Konstruktoren und Instantiierung wegkapselt, erlaubt YUI auch pseudoklassische Vererbung. Die YUI-Helferfunktion Y.extend geht davon aus, dass mit üblichen Konstruktoren gearbeitet wird, mit denen mittels new Objekte (»Instanzen«) erzeugt werden.

Das Beispiel aus der YUI-Dokumentation erzeugt einen Konstruktor namens Bird, mit dem Vögel erzeugt werden können. Die Eigenschaften des zugehörigen Prototypen kennen wir bereits.

function Bird (name) {
    this.name = name;
}
Bird.prototype = {
    flighted: true,
    noise: "Chirp, chirp!",
    sing: function () {
        alert(this.noise);
    }
};

var robin = new Bird("Robin");
robin.sing();
alert("Is " + robin.name + " flighted? " + (robin.flighted ? "yes" : "no"));

Von diesem allgemeinen Objekttyp wird nun ein speziellerer Typ Chicken mit abweichenden Eigenschaften abgeleitet:

function Chicken (name) {
    Chicken.superclass.constructor.call(this, name);
    // dasselbe wie Bird.call(this, name);
}
Y.extend(Chicken, Bird);

Chicken.prototype.flighted = false;
Chicken.prototype.noise = "Cluck, cluck!";
Chicken.prototype.sing = function () {
    alert("The chicken " + this.name + " says:");
    Chicken.superclass.sing.call(this);
    // dasselbe wie Bird.prototype.sing.call(this);
};

var ginger = new Chicken("Ginger");
ginger.sing();
alert("Is " + ginger.name + " flighted? " + (ginger.flighted ? "yes" : "no"));

Der Aufruf von Y.extend stellt die Vererbungshierarchie her. Zwischen Chicken und Bird besteht eine »Ist ein«-Beziehung. Der erste Parameter ist der Konstruktor, dessen Instanzen Fähigkeiten erben, der zweite Parameter der Konstruktor, dessen Prototyp Fähigkeiten bereitstellt.

Der Chicken-Prototyp überschreibt drei Eigenschaften, darunter eine Methode. Im Gegensatz zu Pseudoklassen in Mootools und Prototype stellt Y.extend keinen direkten Super-Verweis bereit. Das heißt, die gleichnamige Methode des allgemeineren Prototypen ist nicht über eine Instanzeigenschaft (wie bei Mootools) oder einen Methodenparameter (wie bei Prototype) zugänglich. Stattdessen erzeugt Y.extend eine Eigenschaft namens superclass beim Chicken-Konstruktor. Diese Eigenschaft verweist auf den Prototypen von Bird. Um diese Funktionen im Kontext der Instanz auszuführen, kann call oder apply verwendet werden.

Dieser superclass-Verweis soll eine Vereinfachung darstellen, alternativ kann man direkt auf Bird bzw. Bird.prototype zugreifen, wie das obige Beispiel zeigt. Y.extend verzichtet hier absichtlich auf Komfort und »Syntactic Sugar«, sondern setzt auf nur unwesentlich vereinfachtes Low-Level-JavaScript. Der Vorteil ist, dass sich YUI damit einige Probleme vom Hals hält. Mootools und Prototype müssen einige Verrenkungen machen, um Super-Aufrufe wie in klassenbasierter OOP zu ermöglichen. Super-Aufrufe kommen nicht allzu häufig vor, daher halte ich YUIs Entscheidung für in Ordnung.

Interne Funktionsweise von Y.extend

Spannend ist nun, was Y.extend intern macht. Es sind im Grunde vier Schritte:

  1. var chickenPrototype = Y.Object(Bird.prototype);

    Mittels dem beschriebenen Y.Object wird ein neues, leeres Objekt angelegt, das Bird.prototype als Prototypen hat.

  2. Chicken.prototype = chickenPrototype;

    Dieses neue Objekt, das durch prototypische Vererbung über die Fähigkeiten von Bird.prototype verfügt, wird der neue Chicken-Prototyp. Danach kann man Chicken.prototype erweitern und Eigenschaften »überschreiben«, ohne dass Bird.prototype angetastet wird.

  3. Chicken.prototype.constructor = Chicken;

    Die constructor-Eigenschaft für Chicken-Instanzen wird auf die richtige Funktion, nämlich Chicken gesetzt.

  4. Chicken.superclass = Bird.prototype;

    Die Konstruktorfunktion Chicken bekommt eine Eigenschaft superclass, welche auf Bird verweist.

Diese Schritte erzeugen eine Vererbungshierarchie über die Prototypen-Kette. Die Kette sieht letztlich so aus:

ginger

[[Prototype]]Chicken.prototype
name"Ginger"
constructorChicken

Chicken.prototype

[[Prototype]]Bird.prototype
flightedfalse
noise"Cluck, cluck!"
singfunction () { … }
constructorObject

Bird.prototype

[[Prototype]]Object.prototype
flightedtrue
noise"Chirp, chirp!"
singfunction () { … }
constructorObject

Object.prototype

[[Prototype]]null
constructorObject
toString[native Funktion]
toLocaleString[native Funktion]
valueOf[native Funktion]
hasOwnProperty[native Funktion]
isPrototypeOf[native Funktion]
propertyIsEnumerable[native Funktion]

Die Chicken-Instanz ginger hat also drei Prototypen, nämlich 1. Chicken.prototype, 2. Bird.prototype und schließlich der Prototyp aller Objekte, Object.prototype.

Aus diesem Grund funktioniert auch die Anwendung des instanceof-Operators:

alert(ginger instanceof Chicken); // true
alert(ginger instanceof Bird);    // true

instanceof schaut, ob der Prototyp der angegebenen Funktion (also Chicken.prototype bzw. Bird.prototype) in der Prototype-Kette des angegebenen Objektes (ginger) existiert.

Die Vererbungshierarchie, die wir direkt mit Y.Object angelegt haben, sieht übrigens genauso aus. Es fehlt dort lediglich die Trennung zwischen Instanzen, Konstruktoren und Prototypen, stattdessen gibt es nur Objekte, die andere Objekte als Prototyp besitzen.

Weitere Parameter von Y.extend: Kompaktschreibweise und statische Eigenschaften

Y.extend nimmt zwei weitere optionale Parameter entgegen. Beide sind Objekte.

Mit dem dritten Parameter lassen sich Eigenschaften angeben, die an den abgeleiteten Prototyp gehängt werden. Dies erlaubt folgende kompakte Schreibweise:

var Chicken = Y.extend(
    // Konstruktor
    function () {
        Chicken.superclass.constructor.call(this, name);
    },

    // Super-Konstruktor
    Bird,

    // Chicken-Prototyp
    {
        flighted: false,
        noise: "Cluck, cluck!",
        sing: function () {
            alert("The chicken " + this.name + " says:");
            Chicken.superclass.sing.call(this, name);
            // dasselbe wie Bird.prototype.sing.call(this, name);
        }
    }
);

var ginger = new Chicken("Ginger");
ginger.sing();
alert("Is " + ginger.name + " flighted? " + (ginger.flighted ? "yes" : "no"));

Das obige Beispiel macht genau dasselbe wie das erste Beispiel. Y.extend gibt immer den ersten Parameter zurück, also die Konstruktorfunktion. Wir können sie als anonyme Funktion übergeben und den Rückgabewert von Y.extend in einer Variable speichern.

Mit dem vierten Parameter von Y.extend lassen sich Eigenschaften angeben, die an die Konstruktorfunktion selbst gehängt werden (statische Eigenschaften, wie sie in der klassenbasierten OOP genannt werden). Das folgende Beispiel nutzen dies, um Eigenschaften direkt an Chicken zu hängen. Wir nutzen dies für einen Zähler, der die Nummer der erzeugten Chicken-Instanzen registriert:

var Chicken = Y.extend(
    // Konstruktor
    function () {
        Chicken.superclass.constructor.call(this, name);
        Chicken.number++;
    },

    // Super-Konstruktor
    Bird,

    // Chicken-Prototyp
    {
        flighted: false,
        noise: "Cluck, cluck!",
        sing: function () {
            alert("The chicken " + this.name + " says:");
            Chicken.superclass.sing.call(this, name);
            // dasselbe wie Bird.prototype.sing.call(this, name);
        }
    },

    // Eigenschaften von Chicken
    {
        number: 0
    }
);

var ginger = new Chicken("Ginger");
var rocky = new Chicken("Rocky");
alert(Chicken.number + " chicken created");

Y.extend ist sehr einfach aufgebaut, bietet aber alles, was für eine prototypische Vererbungshierarchie nötig ist. Die Helferfunktion wird nahezu überall im YUI-Framework mitsamt seinen dutzenden Modulen verwendet.

Mixins mit Y.augment

Mit Y.augment lassen sich Mixins zwischen Pseudoklassen (Konstruktoren-Prototypen-Paaren) umsetzen. Y.augment kopiert die Eigenschaften, anstatt eine Vererbung mittels Prototypen-Kette aufzusetzen.

Y.augment ist sinnvoll, wenn sich die Prototypen zweier Konstruktoren Funktionalität teilen sollen, ohne dass eine strenge Hierarchie zwischen ihnen besteht wird. Die einfachste Aufrufweise lautet:

Y.augment(Zielkonstruktor, Quellkonstruktor);

In diesem Fall werden die Eigenschaften von Quellkonstruktor.prototype nach Zielkonstruktor.prototype kopiert.

Intern wird dazu Y.mix verwendet, welches weiter unten noch genauer erklärt wird. Es gibt allerdings einen Unterschied: Es wird nicht einfach Y.mix(Zielkonstruktor.prototype, Quellkonstruktor.prototype); aufgerufen. Beim Kopieren der Methoden (Funktionsobjekte) werden die Originalfunktionen jeweils durch eine Wrapperfunktion ersetzt. Wenn diese Wrapperfunktion aufgerufen wird, dann wird die Originalfunktion im Kontext der Instanz ausgeführt. Nach dem ersten Aufruf einer geerbten Methode werden alle Wrapperfunktionen durch die Originalfunktionen ersetzt.

Der Sinn davon ist, dass der Quellkonstruktor automatisch aufgerufen wird, sobald eine geerbte Methode aufgerufen wird. Bei der hierarchischen Vererbung mit Y.extend haben wir den Super-Konstruktor mittels Chicken.superclass.constructor.call(this, name); manuell aufgerufen. Y.augment nimmt uns dies ab. Sobald eine geerbte Methode aufgerufen wird, wird der zugehörige Konstruktor einmal aufgerufen. So ist garantiert, dass der Konstruktor das Objekt initialisiert hat, damit die Methoden auch wie gewünscht funktionieren.

Ein angepasstes Beispiel aus der YUI-Dokumentation. Wir haben einen einfachen Konstruktor samt Prototyp:

function Bird () {
    alert("Bird constructor called");
}
Bird.prototype.sing = function () {
    alert("Chirp, chirp!");
};

Dem Bird-Typ wollen wir nun eine abstrakte Funktionalität Flighted (flugfähig) hinzufügen. Dazu definieren wir ein Mixin ebenfalls mit Konstruktor und Prototyp:

function Flighted () {
    alert("Flighted constructor called");
    // Beispiel für eine Initialisierung:
    this.flying = false;
}
Flighted.prototype = {
    fly: function () {
        alert("I'm flying high!");
        this.flying = true;
    },
    land: function () {
        this.flying = false;
    }
};

Flighted ist nicht dazu gedacht, direkt instantiiert zu werden, stattdessen stellt dieses Konstruktor-Prototype-Paar wiederverwendbare Funktionalität bereit, die in andere Typen »eingemixt« werden kann. Dazu nutzen wir nun Y.augment:

Y.augment(Bird, Flighted);

var bird = new Bird();
bird.sing();
bird.fly();

Beim ersten Aufruf von fly ist dies noch eine Wrapperfunktion, welche den Flighted-Konstruktor sowie die Originalfunktion aufruft. Darin kann etwa eine nötige Initialisierung erfolgen. Vergleichen wir die fly-Methode vor und nach ihrem Aufruf mit der Originalfunktion beim Flighted-Prototypen, so kommen wir der Ersetzung auf die Spur:

Y.augment(Bird, Flighted);

var bird = new Bird();
bird.sing();
alert(bird.fly == Flighted.prototype.fly);
// ... ergibt false, verweist auf eine Wrapperfunktion
bird.fly(); // Ruft indirekt den Flighted-Konstruktor auf
alert(bird.fly == Flighted.prototype.fly);
// ... ergibt true, verweist nun auf die Originalfunktion

Einzelnen Objekten Funktionalität hinzufügen

Y.augment muss nicht mit zwei Konstruktorfunktionen als Parameter aufgerufen werden. Es ist genauso möglich, die Eigenschaften eines Prototyps in ein einfaches Objekt einzumixen, wenn nicht alle Instanzen, sondern nur um eine bestimmte Funktionalität erweitert werden soll:

var hedwig = new Bird();
Y.augment(hedwig, Flighted);
hedwig.fly();

Weitere Y.augment-Parameter: Überschreiben erzwingen und Whitelist

Y.augment nimmt noch drei weitere, optionale Parameter entgegen. Der dritte Parameter vom Typ Boolean gibt an, ob bereits vorhandene Methoden beim Kopieren überschrieben werden sollen. Standard ist false. Mit dem vierten Parameter lässt sich eine weiße Liste (Whitelist) mit Eigenschaftsnamen übergeben, das ist ein Array mit String-Einträgen. Der fünfte Parameter erlaubt die Angabe eines Arrays mit Parametern, die dem vererbenden Konstruktor (im Beispiel Flighted) übergeben werden.

Eigenschaften kopieren mit Y.mix

Y.mix wurde bereits angesprochen: Es ist eine allgemeinere Funktion zum Kopieren von Eigenschaften zwischen zwei Objekten, die intern von Y.extend und vor allem Y.augment verwendet wird. Y.mix ermöglicht nicht nur das Erweitern von Prototypen bzw. das Erweitern der Prototypen-Kette, sondern besitzt verschiedene Kopiermodi. Die YUI-Dokumentation enthält ein Beispiel zum Kombinieren zweier Module, die mit dem Revealing Module Pattern notiert wurden. Das halte ich für einen verbreiteten Anwendungsfall.

var Modul = (function () {
    var modulname = "Modul";
    return {
        methode1: function () {
            alert(modulname + '.methode1 wurde aufgerufen');
        }
    };
})();

var Erweiterung = (function () {
    var modulname = "Erweiterung";
    return {
        methode2: function () {
            console.log(modulname + '.methode2 wurde aufgerufen');
        }
    };
})();

Y.mix(Modul, Erweiterungsmodul);

Modul.methode1();
Modul.methode2();

Bekannt sein sollte hier, dass die einzelnen gekapselten Module nicht gegenseitig auf ihre privaten Objekte zugreifen können.

Auch bei vereinfachter prototypischer Vererbung mit Y.Object bietet sich Y.mix an, um die Funktionalität eines anderen Objektes einzumixen:

var bird = {
    noise: "Chirp, chirp!",
    sing: function () {
        alert(this.noise);
    }
};

var flighted = {
    flying: false,
    fly: function () {
        alert("I'm flying high!");
        this.flying = true;
    },
    land: function () {
        this.flying = false;
    }
};

var robin = Y.Object(bird);
Y.mix(robin, flighted);
robin.sing();
robin.fly();

Y.mix erzeugt standardmäßig eine sogenannte flache Kopie (shallow copy). Das heißt, dass Objekte wie etwa Arrays bloß referenziert werden und keine echte Kopie von ihnen angelegt wird. Für eine tiefe Kopie (deep copy) ist das hier nicht besprochene Y.clone zuständig, für das kontrollierte Erweitern von Eigenschaften vom Typ Objekten und Arrays gibt es die Methode Y.aggregate.

Weitere Parameter von Y.mix: Überschreiben erzwingen und Whitelist

Y.mix nimmt insgesamt sechs Parameter entgegen, davon sind die letzten vier optional. Interessant sind noch der dritte und vierte Parameter, wir haben sie schon bei Y.augment kennengelernt: Der dritte gibt an, ob gleichnamige Eigenschaften beim Kopieren überschrieben werden. Dies ist standardmäßig nicht der Fall und kann mit true erzwungen werden. Der vierte Parameter ermöglicht die Übergabe einer weißen Liste (Whitelist). Diese wird als Filter verwendet, sodass nur die angegebenen Eigenschaften kopiert werden.

Mehrere Objekte vereinen mit Y.merge

Y.merge ist ein kleiner, spezieller Wrapper für Y.mix. Der einzige Unterschied ist, dass beliebig viele Objekte übergeben werden können. Der zweite und alle folgenden Parameter geben Objekte an, deren Eigenschaft in das Objekt kopiert werden, das als erster Parameter übergeben wird:

Y.merge(Modul, Erweiterung1, Erweiterung2, Erweiterung3);

Y.augment vs. Y.mix

Der entscheidende Unterschied zwischen Y.mix und Y.augment, die »Lazy Augmentation« durch den verzögerten Konstruktoraufruf, ist leider nicht ausreichend dokumentiert. Dies mag innerhalb des YUI-Frameworks häufig vorkommen und scheint mir auch ganz klug: Es verzögert die Initialisierung des Mixins bis zu dem Punkt, an dem die vererbten Methoden tatsächlich genutzt werden. Wenn ein solches Setup mittels Konstruktorfunktion nicht nötig ist, gibt es keinen Vorteil von Y.augment gegenüber dem einfachen Y.mix.

Für die vorgestellten Funktionen gibt es relative gute Beispiele in der YUI-Dokumentation, die ich hier adaptiert habe. Darüber hinaus zwei exzellente Blogartikel von Stoyan Stefanov, die ein größeres Bild zeichnen und die Hintergründe noch detaillierter erklären, als ich es hier versucht habe.