Orlok: Nachfrage zu prototypischen Manipulationen

Beitrag lesen

Hallo dedlfix

Äpfel und Birnen miteinander zu vergleichen ist nicht hilfreich, wenn Gleichheit das Ergebnis sein soll. Gäbe es denn für ES5 die Möglichkeit, das so umzusetzen, dass dein (wie mir scheint akademisches, aber was weiß ich schon) Beispiel das gewünschte Ergebnis liefert? Jedenfalls kann die aktuelle Versionen von Typescript auch nach ES6 übersetzen, dann ist das Ergebnis auch Code mit syntaktischen Zucker und die von dir angesprochenen Zusatzstoffe sind automatisch enthalten.

Es ging mir hier nicht um einen Vergleich von TypeScript und ECMAScript. Ich habe lediglich die Aussage von Christian aufgegriffen, wonach er eine von Array abgeleitete Klasse so ähnlich umgesetzt hätte wie in dem Code, zu dem das TypeScript-Beispiel übersetzt wurde. Jedenfalls habe ich ihn so verstanden, zumal er sich dabei auf meinen Beitrag bezogen hatte. Dementsprechend ging es mir nur darum zu zeigen, dass eine solche Implementierung nicht funktionieren kann.

Möglicherweise habe ich Christian aber auch falsch verstanden und er bezog sich nicht auf den konkreten Fall, der Gegenstand meiner Antwort an Felix war, sondern er meinte die Erstellung von abgeleiteten Klassen ganz allgemein. In diesem Fall wäre mein Beitrag vermutlich überflüssig.

Der Ursprung der ganzen Diskussion liegt übrigens darin, dass ich, obwohl ich tatsächlich nur über akademisches Wissen verfüge und nicht auf zwanzig Jahre Entwicklertätigkeit zurückblicken kann, es dennoch für nötig befunden habe, in meinem Artikel der Frage nachzugehen, wie man eigene Methoden für Arrays definieren kann, ohne das Standardobjekt Array.prototype zu manipulieren, welches sich alle eingebundenen Programme und möglicherweise beteiligten Entwickler teilen. Ich habe also versucht zu erklären, warum die Veränderung von Standardobjekten, von Polyfills einmal abgesehen, eine schlechte Praxis ist, habe das Risiko von Namenskonflikten benannt und versucht Alternativen aufzuzeigen.

Dabei bin ich zu dem Ergebnis gekommen, dass die selbstdefinierten Methoden idealerweise auf einem prototypischen Objekt angelegt werden sollten, welches in der Prototypenkette zwischen den Arrays und dem Objekt Array.prototype einzufügen ist, sodass die Arrays sowohl die nativen Methoden, als auch die selbstdefinierten Methoden erben, ohne dass Array.prototype manipuliert wird und ohne die Methoden auf jeder einzelnen Instanz anlegen zu müssen, oder die Funktionalität anderswohin zu delegieren.

Diese Lösung ist allerdings ohne die Syntax mit class und extends nicht zu verwirklichen, es sei denn, man fügt das prototypische Objekt mit den selbstdefinierten Methoden nach der Erzeugung der Instanzen in deren zu diesem Zeitpunkt bereits etablierte Prototypenkette ein, was aus Gründen der Performanz im Allgemeinen nicht zu empfehlen ist. Womit deine Frage wohl beantwortet wäre.

Nicht beantwortet ist damit aber die Frage, was denn nun als best practice für die Definition eigener Arraymethoden zu empfehlen sei. Hier bin ich zu dem Ergebnis gekommen, dass ich meinen Artikel nicht für die Leser von gestern, sondern für die Leser von morgen schreibe und entsprechend habe ich die Methodendefinition innerhalb einer von Array abgeleiteten Klasse empfohlen, wie es nach dem aktuellen Standard der Sprache möglich ist. Darüber hinaus hatte Felix in diesem Thread gefragt, wie in einer solchen Situation in Zukunft zu verfahren sei, also habe ich hier dieselbe Antwort gegeben.

Tempus fugit, aber wie ist die Situation heute? Die Situation ist, dass die Klassensyntax, wie sie in der sechsten Edition der Sprache standardisiert wurde, noch nicht von allen Ausführungsumgebungen unterstützt wird, die für ein aktuelles Projekt von Bedeutung sind. Alternative Lösungsmöglichkeiten können also zumindest für den Moment nicht einfach ausgeblendet werden. Die Frage ist also zur Zeit noch, was mache ich, wenn ich keine von Array abgeleitete Klasse erstellen kann?

Diese Frage ist nicht leicht zu beantworten. Nicht nur das. Ich denke, sie ist pauschal überhaupt nicht zu beantworten, denn jede mögliche Alternative geht mit ganz eigenen Nachteilen einher, die unter verschiedenen Voraussetzungen unterschiedlich ins Gewicht fallen. Welche Vorgehensweise zu empfehlen ist, hängt also ganz wesentlich von dem konkreten Projekt ab, um das es sich handelt, und Ratschläge können hier dementsprechend meiner Ansicht nach nur in der Form gegeben werden, die verschiedenen Alternativen zu benennen und ihre Vor- und Nachteile aufzuzeigen.

Da ich diese Situation also nicht ignorieren konnte, habe ich in meinem Artikel mehrere Möglichkeiten präsentiert, wie Methoden definiert werden können, ohne dabei im globalen Namensraum seine Spuren zu hinterlassen und ohne sich dabei der Syntax mit class und extends zu bedienen.

Hier bereits genannt habe ich die Alternativen, die Methoden auf den Instanzen selbst zu definieren, oder ein Objekt mit den Methoden nach Erzeugung der Instanzen in deren Prototypenkette zu injizieren. Erstgenanntes geht mit dem Nachteil des erhöhten Speicherverbrauchs einher, letztgenanntes mit mehr oder weniger erheblichem zeitlichen Mehraufwand bei den meisten auf dem Objekt ausgeführten Operationen. Welche der beiden Varianten kostspieliger ist, kann man ohne entsprechende Tests mit einem konkreten Programm wohl nicht beantworten. Jedenfalls kann ich es nicht.

Eine weitere Möglichkeit, potentielle Namenskonflikte und dadurch verursachte Fehler zu vermeiden, könnte natürlich darin liegen, die Methoden zwar auf Array.prototype zu definieren, ihnen aber derart kryptische oder spezielle Namen zu geben, dass eine mehrfache Definition unwahrscheinlich ist. Das wird womöglich in der Mehrzahl der Fälle eine hinreichend sichere Lösung sein, aber ein Risiko besteht hier dennoch und unter Umständen wird dabei die Les- und Wartbarkeit des Codes beeinträchtigt.

Weiterhin können die Methoden so definiert werden, dass sie nicht direkt auf einem Array operieren, sondern sie zunächst ein Array referenzieren, das in einem gewöhnlichen Objekt gekapselt ist, über dessen Prototyp die Methoden vererbt werden. Im Gegensatz zu anderen Formen der Delegation würde so eine Lösung noch halbwegs semantischen Code ergeben. Der direkte Zugriff kann hierbei aber natürlich nur über einen entsprechenden Elementausdruck erfolgen, gegebenenfalls über zu diesem Zweck definierte Methoden, was alles andere als elegant ist.

Abenteuerlustige Programmierer könnten darüber hinaus, zumindest in Browsern, auch einen iframe erstellen und mithin ein neues, frisches Set Standardobjekte, dessen Konstruktor Array und dessen prototypisches Objekt man exklusiv für sich beanspruchen könnte. Performant ist allerdings anders. Zudem hätte man den Nachteil, dass Operationen, die sich auf die Prototypenkette der auf diese Weise erstellten Arrays beziehen, unter Umständen zu unerwünschten Ergebnissen kommen. Eine solche Vorgehensweise kann demnach wohl kaum als ernsthafte Alternative in Erwägung gezogen werden.

Die letzte Möglichkeit die ich hier sehe, führt uns wieder an den Anfang der Überlegungen zurück, beziehungsweise zum Ursprung des Problems, nämlich dass es in ECMAScript 5 nur möglich ist, entweder ein Array zu erstellen, dessen Prototyp Array.prototype ist, oder aber ein gewöhnliches Objekt, dem die spezielle Semantik von Arrays fehlt, das aber über die gewünschte Prototypenkette verfügt.

Da nun Getter und Setter bereits seit ECMAScript 5 Teil des Standards sind und die Kompatibilität diesbezüglich gegeben sein dürfte, wäre es möglich statt native Arrays zu verwenden, deren Semantik auf planen Objekten nachzubauen. Das heißt, man könnte das besondere Verhalten hinsichtlich der Eigenschaft length und solcher Eigenschaften, deren Name ein numerischer Index ist, nachbilden.

Das wäre tatsächlich ohne allzu großen Aufwand möglich, allerdings könnte man hierbei nicht jedes Detail berücksichtigen, sodass darüber hinaus auch die ein oder andere native Arraymethode nachgebaut werden müsste. Ein solches Vorgehen wäre wohl nur in Ausnahmefällen anzuraten.

Weitere Lösungsmöglichkeiten fallen mir ehrlich gesagt nicht ein. Aber vielleicht müssen sie das ja auch nicht, weil das Problem rein akademisch ist. Womöglich habe ich beim Schreiben des Artikels und der Beiträge in diesem Thread einfach nur meine Zeit verschwendet, und die der Leser. Ob dem so ist, kann mir vielleicht jemand beantworten, der über mehr Praxiserfahrung verfügt als ich.

Gruß,

Orlok