JavaScript/Objekte/Object

Aus SELFHTML-Wiki
Wechseln zu: Navigation, Suche

Mit dem eingebauten Konstruktor Object können plane Objekte erzeugt werden. Wird ihm beim Aufruf ein primitiver Wert übergeben, so wandelt er diesen in ein spezifisches Objekt um. Die Methoden des in seiner Eigenschaft prototype hinterlegten Objektes werden standardmäßig über die Prototypenkette an alle anderen Objekte vererbt. Darüber hinaus verfügt Object über verschiedene eigene Methoden, welche für die objektorientierte Programmierung äußerst nützliche Werkzeuge darstellen.


Syntax

Object([value])


Attribute
Writable true
Enumerable false
Configurable true



Referenzierung

Die Funktion Object ist eine Methode des globalen Objektes, also in dem üblichen Fall, dass es sich bei der Ausführungsumgebung um einen Webbrowser handelt, der Wert der gleichnamigen Eigenschaft des Window-Objekts. Analog dazu ist Object zum Beispiel in der Ausführungsumgebung Node.js eine Methode von global. Da die öffentlichen Eigenschaften des globalen Objektes in der Regel zugleich auch Teil der globalen lexikalischen Umgebung sind, kann Object nicht nur als Objekteigenschaft referenziert werden, sondern ebenfalls als globale Variable.

Bedeutung

Object ist von fundamentaler Bedeutung innerhalb der Sprache, denn im Allgemeinen sind alle Objekte Instanzen dieses Konstruktors, unabhängig davon ob es sich dabei um gewöhnliche oder um sogenannte exotische Objekte handelt, wie beispielsweise Arrays. Das bedeutet unter anderem, dass jedes Objekt die von Object.prototype bereitgestellten Methoden erbt, sofern nicht explizit etwas anderes bestimmt wurde.



Dass in der Regel alle nicht primitiven Werte Instanzen von Object sind, kann im Übrigen unter Verwendung des Operators instanceof überprüft werden, wobei es in diesem Zusammenhang durchaus interessant ist, dass Object.prototype hier eine Ausnahme darstellt. Für das in der Eigenschaft prototype des Konstruktors hinterlegte Objekt wird im Gegensatz zu allen anderen Standardobjekten bei der Prüfung false zurückgegeben, da dessen Prototyp null ist. Aus dem selben Grund gibt der Test bei solchen Objekten false zurück, für die bei ihrer Erzeugung explizit null als Prototyp bestimmt wurde. Diese Objekte erben dem zur Folge auch nicht die Eigenschaften und Methoden von Object.prototype.


Beispiel
console.log([2, 4, 8] instanceof Object); // true

console.log(Object.create(null) instanceof Object); // false


Für alle anderen Objekte die von Object.prototype erben sowie für Object.prototype selbst gilt, dass mit den entsprechenden Methoden zum Beispiel geprüft werden kann, ob das Objekt über eine eigene Eigenschaft mit einem bestimmten Bezeichner verfügt, oder auch ob eine seiner Eigenschaften abzählbar ist. Ebenso kann examiniert werden, ob das Objekt auf dem die entsprechende Methode aufgerufen wurde der Prototyp eines anderen Objektes ist. Dabei ist allerdings anzumerken, dass einige dieser Methoden entlang der Prototypenkette standardmäßig durch spezifischere, an den jeweiligen Objekttyp angepasste Methoden mit demselben Namen überschrieben werden.


Davon abgesehen repräsentiert der Konstruktor Object selbst den grundlegendsten Typ von Objekten, nämlich den Typ Object, dessen Semantik derjenigen gewöhnlicher Objekte entspricht und der dadurch charakterisiert ist, dass er keine über dieses Standardverhalten hinausgehende Funktionalität besitzt, weshalb Objekte dieses Typs auch plane Objekte genannt werden.



Der Konstruktor Object stellt jedenfalls eine Bibliothek verschiedener Methoden für die Arbeit mit Objekten zur Verfügung, die es beispielsweise ermöglichen die internen Attribute von Eigenschaften zu manipulieren, die Eigenschaften eines Objektes auf ein anderes Objekt zu kopieren, oder auch bei der Erzeugung eines Objektes einen beliebigen Prototypen zu bestimmen. Auch kann die Erweiterung oder Veränderung eines Objektes verhindert werden und es stehen darüber hinaus verschiedene Methoden zur Verfügung, Informationen über ein Objekt zu gewinnen. Hinsichtlich der Handhabung von Objekten wird durch Object also ein wesentlicher Teil der eingebauten Funktionalität bereitgestellt.

Erzeugung planer Objekte

Wird der Konstruktor Object ohne Argumente aufgerufen, sodass der Funktionsparameter mit undefined initialisiert wird, oder ist der Wert des beim Aufruf übergebenen Argumentes null oder undefined, dann wird ein neues planes Objekt erzeugt und zurückgegeben. Der Wert der internen Eigenschaft [[Prototype]] des erzeugten Objektes ist dabei grundsätzlich eine Referenz auf Object.prototype.


Beispiel
const object = new Object;

console.log(typeof object); // object


In diesem Beispiel wird zunächst durch den Aufruf von Object als Konstruktor ein neues Objekt erzeugt. Dieses Objekt wird dann einer Konstante als Wert zugewiesen. Als nächstes wird unter Verwendung des Operators typeof geprüft, welchen Datentyp der von Object zurückgegebene Wert hat, und da hier wie gesehen ein Objekt erzeugt wurde, wird entsprechend die Zeichenkette object in die Konsole geschrieben.


Dabei sei übrigens darauf hingewiesen, dass bei dem Aufruf von Object die runden Klammern nach dem Bezeichner absichtlich weggelassen wurden. Dies ist immer dann möglich, wenn eine Funktion als Konstruktor aufgerufen wird, ohne dass dabei Argumente übergeben werden. Denn der Aufruf als solcher ist bereits Teil der Semantik des Operators new. Das bedeutet, die runden Klammern ermöglichen in diesem Fall lediglich die Übergabe von Argumenten, aber sie sind für den Aufruf der Funktion nicht erforderlich.


Beispiel
const object = Object( );

const prototype = Object.prototype;

console.log(Object.getPrototypeOf(object) == prototype); // true


Hier wird zunächst ebenfalls durch den Aufruf von Object ein Objekt erzeugt und einer Konstante als Wert zugewiesen, jedoch wird Object in diesem Beispiel nicht unter Verwendung des Operators new als Konstruktor aufgerufen, sondern einfach als Funktion. Das ist möglich, da sich Object unabhängig vom Muster des Aufrufs identisch verhält. Dabei spielt es auch keine Rolle, ob und falls ja welche Argumente beim Aufruf übergeben werden, sodass Object grundsätzlich als normale Funktion aufgerufen werden kann.


Hinweis:
Aus dem Umstand, dass Object bei einem normalen Funktionsaufruf dieselbe Funktionalität bereitstellt wie bei einem Aufruf als Konstruktor, sollte nicht der Schluss gezogen werden, dass sich dieses Schema auf alle Standardobjekte übertragen ließe die gleichzeitig Konstruktorfunktionen sind. Zwar kann beispielsweise auch der Konstruktor Array als Funktion aufgerufen werden und es wird eine neue Instanz erzeugt und zurückgegeben, aber andere eingebaute Konstruktoren wie zum Beispiel String verhalten sich bei einem gewöhnlichen Funktionsaufruf anders als bei einem Aufruf mittels new.


Weiterhin wird in dem Beispiel oben eine Referenz auf das Objekt Object.prototype in der Konstante mit dem Bezeichner prototype hinterlegt. Diese wird dann bei der abschließenden Prüfung verwendet, bei der zunächst mit der Methode getPrototypeOf des Konstruktors Object der Wert der internen Eigenschaft [[Prototype]] des zuvor erstellten Objektes ermittelt wird, also dessen Prototyp. Dieser Wert wird dann mit dem Wert der Konstante prototype verglichen und bei der Ausgabe in der Konsole wird bestätigt, dass Object.prototype tatsächlich der Prototyp des neu erstellten Objektes ist.


Beispiel
(function (Object) {

  const object = window.Object( );

}(null));


In diesem Beispiel wird Object nun als Methode aufgerufen, also beim Aufruf explizit über das globale Objekt window referenziert. Dies ist hier nötig, weil der Zugriff innerhalb einer Funktion erfolgt, die über eine lokale Variable mit demselben Bezeichner verfügt.


Bei diesem unmittelbar aufgerufenen Funktionsausdruck wird nämlich zunächst ein Parameter mit dem Namen Object deklariert und dieser danach mit dem Argument null initialisiert. Da nun Funktionsparameter letztlich nichts anderes sind als lokale Variablen, würde der Versuch Object ohne die explizite Referenz auf window aufzurufen einen Type Error produzieren, denn bei der internen Auflösung der Bezeichner würde hier die Variable aus der lexikalischen Umgebung des laufenden Ausführungskontextes mit dem Wert null referenziert, und nicht die globale Variable Object.




Wie gesehen können mittels Object also gewöhnliche Objekte erzeugt werden, jedoch sei grundsätzlich empfohlen, die Erzeugung solcher Objekte entweder über die Literalschreibweise (Object Initializer) oder aber unter Verwendung der Methode create von Object vorzunehmen, welche es zudem ermöglicht, für das zu erzeugende Objekt einen Prototypen zu bestimmen, um auf diese Weise die Vererbung von Eigenschaften und Methoden zu implementieren oder umgekehrt zu verhindern, dass Eigenschaften und Methoden über die Prototypenkette an das erzeugte Objekt vererbt werden.


Beispiel
const prototype = {
  logTrue ( ) {
    console.log(true);
  }
};

const object = Object.create(prototype);

object.logTrue( ); // true


In diesem Beispiel wird zunächst durch ein Objektliteral ein gewöhnliches Objekt erzeugt, welches dann der Konstante mit dem Bezeichner prototype als Wert zugewiesen wird. Bei dieser Initialisierung wird auf dem Objekt eine Methode definiert. Diese hat den Bezeichner logTrue und enthält die Anweisung, den Wert true in die Konsole zu schreiben. Im nächsten Schritt wird dann ein zweites Objekt erzeugt, diesmal unter Verwendung der Methode Object.create, wobei eine Referenz auf das erste Objekt als Argument übergeben wird.


Dies hat zur Folge, dass prototype nunmehr der Wert der internen Eigenschaft [[Prototype]] des neuen Objektes mit dem Bezeichner object ist, sodass dieses die Eigenschaften und Methoden von prototype erbt. Weil zu diesen geerbten Eigenschaften natürlich auch die Methode logTrue gehört, wird diese bei ihrem abschließenden Aufruf auf object korrekt referenziert, mit der Folge, dass im Ergebnis true in die Konsole geschrieben wird.

Typumwandlung

Wird Object beim Aufruf ein Argument übergeben dessen Wert weder null noch undefined ist, so wird für diesen Wert intern die abstrakte Operation ToObject ausgeführt, was bedeutet, dass wenn ein primitiver Wert als Argument übergeben wurde, dieser in ein spezifisches Objekt umwandelt wird (Object Wrapper). Der übergebene Wert selbst wird dabei in einer internen Eigenschaft des erzeugten Objektes hinterlegt, welches verschiedene dem jeweiligen Datentyp angepasste Methoden bereitstellt, mittels derer dann auf diesem Wert operiert werden kann.


Beispiel
const number = Math.PI;

const object = Object(number);

console.info(object.toFixed(3)); // 3.142


In diesem Beispiel wird zunächst eine Konstante mit dem Bezeichner number deklariert und ihr der angenäherte Wert der Kreiszahl Pi als Wert zugewiesen, welcher in der gleichnamigen Eigenschaft des Standardobjektes Math hinterlegt ist. Hierbei handelt es sich um einen primitiven Wert vom Datentyp Number.


Dieser Wert wird nun in einem zweiten Schritt als Argument an Object übergeben und es wird wie oben gesehen eine Typumwandlung durchgeführt, sodass infolgedessen der in der Konstante object hinterlegte Rückgabewert der Funktion nun vom Datentyp Object ist. Der primitive Wert selbst ist in der internen Eigenschaft [[NumberData]] des Objektes hinterlegt und könnte unter Verwendung der von Number.prototype geerbten Methode valueOf ausgegeben werden. Dabei ist allerdings anzumerken, dass man sich diesen Methodenaufruf auch sparen kann, da er bei einer Ausgabe implizit erfolgt.



In einem letzten Schritt wird in dem Beispiel dann auf object die ebenfalls von Number.prototype geerbte Methode toFixed aufgerufen, wobei die Zahl 3 als Argument übergeben wird. Dieser Aufruf sorgt dafür, dass die in dem Objekt gespeicherte mathematische Konstante auf drei Nachkommastellen gerundet wird, sodass schließlich eine Stringrepräsentation des Wertes 3.142 in die Konsole geschrieben wird.


Hinweis:
Statt die Typumwandlung durch Object vornehmen zu lassen könnte man zu diesem Zweck die entsprechenden Konstruktoren wie beispielsweise Number oder String natürlich auch direkt aufrufen. Allerdings sollte grundsätzlich davon abgesehen werden, eine solche Umwandlung überhaupt manuell vorzunehmen, egal ob durch Object oder einen anderen Konstruktor. Dies ist nämlich nicht erforderlich, um auf einem primitiven Wert entsprechende Operationen auszuführen, da beim Aufruf einer für den jeweiligen Datentyp bereitgestellten Methode direkt auf dem Wert selbst, die Umwandlung in ein spezifisches Objekt automatisch erfolgt.


Im folgenden Beispiel wird im Ergebnis ebenso wie beim letzten Beispiel eine Stringrepräsentation des Wertes 3.142 erzeugt, mit dem Unterschied, dass hier die Typumwandlung vom primitiven Wert in ein Objekt implizit erfolgt.


Beispiel
const fixed = Math.PI.toFixed(3);

console.info(fixed); // 3.142


Wie gesehen handelt es sich beim Wert der Eigenschaft PI von Math um einen primitiven Wert und nicht um ein Objekt. Wenn nun die Methode toFixed direkt auf diesem Wert aufgerufen wird, dann wird jedoch kein Type Error produziert, sondern es wird automatisch eine Umwandlung in ein entsprechendes spezifisches Objekt vom Typ Number durchgeführt, sodass die Methode auf den Wert angewendet werden kann. Eine explizite Umwandlung durch Object ist dem zur Folge also nicht notwendig.


Wird Object aufgerufen und dabei kein primitiver Wert sondern ein Objekt als Argument übergeben, so wird nach den Regeln von ToObject dieses Objekt einfach unverändert wieder zurückgegeben, wie das folgende Beispiel zeigt.


Beispiel
const jekyll = { };

const hyde = Object(jekyll);

console.log(jekyll == hyde); // true


Hier wird zunächst in Literalschreibweise ein leeres Objekt erzeugt und der Konstante mit dem Bezeichner jekyll als Wert zugewiesen. Eine Referenz auf dieses Objekt wird danach dem Konstruktor Object bei seinem Aufruf als Argument übergeben und der Rückgabewert in der Konstante hyde gespeichert. Als Ergebnis der Überprüfung auf Gleichheit wird schließlich true in die Konsole geschrieben, da beide Konstanten auf dasselbe Objekt verweisen. Das als Argument an Object übergebene Objekt wurde also einfach wieder zurückgegeben.




Zusammenfassend lässt sich bezüglich des Konstruktors Object also festhalten, dass sich sein praktischer Nutzen primär aus den mit ihm verknüpften Methoden ergibt, sowie aus denjenigen Methoden, die durch das in seiner Eigenschaft prototype hinterlegte Objekt an andere Objekte vererbt werden. Für seine Funktion als Konstruktor planer Objekte gibt es hingegen wie gesehen bessere Alternativen, und die Möglichkeit mittels Object eine Typumwandlung durchzuführen wird kaum je benötigt, da eine solche Umwandlung im Bedarfsfall implizit durchgeführt wird.


Abschließend sei außerdem erwähnt, dass es zwar möglich ist Object eigenhändig um weitere Eigenschaften und Methoden zu erweitern, aber auch, dass hiervon grundsätzlich abzuraten ist, denn es ist in der Regel nicht absehbar, welche Seiteneffekte durch die Manipulation von eingebauten Objekten gegebenenfalls später noch entstehen. Deshalb ist es grundsätzlich eine schlechte Praxis, an Standardobjekten Veränderungen vorzunehmen, die über die Bereitstellung von Polyfills für unter Umständen von der Ausführungsumgebung nicht unterstützte standardkonforme Methoden hinausgehen.

Spezifikation

The Object Constructor ECMAScript 2015 ECMAScript 2016 ECMAScript 2017 Draft
ObjectCreate ECMAScript 2015 ECMAScript 2016 ECMAScript 2017 Draft

Weblinks