JavaScript/Objekte/Object/toString

Aus SELFHTML-Wiki
Wechseln zu: Navigation, Suche

Die Methode mit dem Bezeichner toString gibt eine Zeichenkette zurück, welche den Wert repräsentiert, in dessen Kontext sie aufgerufen wurde. Sie wird implizit immer dann aufgerufen, wenn es nötig ist, ein Objekt in einen String zu konvertieren. Die Methode kann darüber hinaus zur Typbestimmung benutzt werden und unter Verwendung einer eingebauten Konstante ist es möglich, die zurückgegebene Zeichenkette individuell anzupassen.


Syntax

Object.prototype.toString( )


Attribute
Writable true
Enumerable false
Configurable true


Prototyp


Eigenschaften


Beschreibung

Die Methode toString besitzt keine formalen Parameter, das heißt, sie erwartet beim Aufruf keine Argumente. Der Wert auf dem sie operiert wird vielmehr durch den Kontext bestimmt, in dem die Methode ausgeführt wird, also durch den Wert, mit dem die Kontextvariable this der Methode beim Aufruf initialisiert wird. Zurückgegeben wird von der Methode toString eine Zeichenkette, welche diesen Wert repräsentiert.


Beispiel
const object = { };

console.log(object.toString( )); // [object Object]


Der zurückgegebene String ist dabei grundsätzlich aus denselben Komponenten zusammengesetzt, nämlich in eckige Klammern gefasst zunächst das Prefix object, ein Leerzeichen und schließlich ein Tag mit dem Namen des jeweiligen Typs, welcher, anders als dies etwa beim Operator typeof der Fall ist, standardmäßig großgeschrieben wird. Wird die Methode toString wie in dem Beispiel oben über einen Elementausdruck auf einem planen Objekt aufgerufen, sodass die Kontextvariable this der Methode mit diesem Objekt initialisiert wird, dann wird bei der Konkatenation des Strings, dem Typ des Objektes entsprechend, das Tag Object eingefügt.


Beispiel
const array = [ ];

console.log({ }.toString === [ ].toString); // false

console.log(Object.prototype.toString.call(array)); // [object Array]


Soll die Objektmethode toString wie in dem Beispiel oben im Kontext eines Arrays aufgerufen werden, dann muss das Array beim Aufruf explizit als Wert für this bestimmt werden, da es für Arrays eine spezifischere Methode mit dem Namen toString gibt, welche die Methode von Object.prototype innerhalb der Prototypenkette verschattet. Ein Aufruf der Objektmethode toString im Kontext eines Arrays kann wie hier unter Verwendung der Methode call erfolgen, die als erstes Argument den Wert erwartet, mit dem die Kontextvariable this initialisiert werden soll. Anders als bei einer Prüfung mit dem Operator typeof, bei der Arrays wie gewöhnliche Objekte behandelt werden, wird von der Methode toString das Tag Array in den zurückgegebenen String eingebaut.


Beispiel
function test ( ) {
  console.log(arguments.toString( ));
}

test( ); // [object Arguments]


Es wird jedoch nicht für jeden Objekttyp eine eigene Methode mit dem Namen toString bereitgestellt. Wird die Methode zum Beispiel auf dem Objekt aufgerufen, das innerhalb von Funktionen über den Bezeichner arguments referenziert werden kann und das die beim Aufruf an die Funktion übergebenen Argumente enthält, dann wird die Methode von Object.prototype referenziert. Ein Aufruf über call oder apply ist demnach also nicht notwendig, um die Objektmethode toString im Kontext dieses Objektes auszuführen. Wird toString auf arguments aufgerufen, dann wird ein String zurückgegeben, der dem allgemeinen Schema entspricht und der dem spezifischen Typ des Objektes gemäß das Tag Arguments beinhaltet.


Beispiel
const test = function ( ) { };

console.log({ }.toString.call(test)); // [object Function]


Wird die Methode toString im Kontext eines Funktionsobjektes aufgerufen, dann wird bei der Konkatenation des zurückgegebenen Strings grundsätzlich das Tag Function eingefügt, unabhängig davon, um was für eine Funktion es sich handelt, also ob eine gewöhnliche Funktion, eine Pfeilfunktion oder eine Klasse geprüft wird. Anders als dies bei anderen Objekten der Fall ist, von planen Objekten einmal abgesehen, entspricht das eingebaute Tag hier dem String, welcher bei einer Prüfung mit dem Operator typeof zurückgegeben wird, mit dem Unterschied, dass der Typbezeichner hier großgeschrieben wird.


Beispiel
console.log({ }.toString.call(new String)); // [object String]

console.log({ }.toString.call(true)); // [object Boolean]


Soll die Methode toString zu Typbestimmung verwendet werden ist jedoch zu beachten, dass wenn die Methode im Kontext eines primitiven Wertes ausgeführt wird, dieser Wert zunächst in ein spezifisches Objekt konvertiert wird, sofern dies möglich ist, bevor die eigentliche Prüfung erfolgt. Es kann also anhand des Rückgabewertes der Methode nicht unterschieden werden, ob deren Kontextvariable mit einem primitiven Wert oder einem spezifischen Objekt initialisiert wurde, das eingefügte Tag also den Datentyp oder den Objekttyp widergibt.


Beispiel
console.log({ }.toString.call(Symbol( ))); // [object Symbol]

console.log({ }.toString.call(Infinity)); // [object Number]


Die Konvertierung in ein spezifisches Objekt erfolgt dabei, wie unter anderem auch beim Aufruf des eingebauten Konstruktors Object, nach den Regeln der abstrakten Operation ToObject, das bedeutet, es wird durch den impliziten Aufruf des entsprechenden Konstruktors ein sogenanntes Wrapper Object erzeugt, welches den primitiven Wert in einer internen Eigenschaft kapselt. Da es jedoch für die primitiven Datentypen Null und Undefined keine eingebauten Konstruktoren gibt, welche ein entsprechendes spezifisches Objekt erzeugen könnten, sieht die Operation ToObject für diese Fälle eigentlich vor, eine Ausnahme zu werfen. Wie das folgende Beispiel zeigt, geschieht dies beim Aufruf der Methode toString im Kontext eines solchen primitiven Wertes jedoch nicht.


Beispiel
console.log({ }.toString.call(undefined)); // [object Undefined]

console.log({ }.toString.call(null)); // [object Null]


Stattdessen wird auch hier der übliche String zurückgegeben und es wird abhängig vom übergebenen Wert das Tag Undefined oder das Tag Null eingefügt. Dabei erscheint die Verwendung des Prefixes object auf den ersten Blick ziemlich unsinnig, zumal es sich hier in keinster Weise um Objekte handelt, also weder ein Objekt an this gebunden wird, noch eine implizite Konvertierung in ein spezifisches Objekt überhaupt möglich ist. Es ist dabei jedoch zu berücksichtigen, dass bei einer Abweichung vom allgemeinen Schema, auch wenn dies mit Blick auf die Semantik in diesen beiden Fällen eigentlich angebracht wäre, Stringvergleiche aufwändiger zu implementieren wären, was die Nutzbarkeit der Methode einschränken würde.


Beispiel
console.log({ }.toString.call(new Error)); // [object Error]

console.log({ }.toString.call(new TypeError)); // [object Error]


Schließlich ist zu berücksichtigen, dass nicht alle eingebauten Objekttypen über ein eigenes Tag verfügen, welches bei der Zusammensetzung des von toString zurückgegebenen Strings eingefügt werden könnte. Bei Fehlerobjekten wird zum Beispiel immer das Tag Error verwendet, unabhängig davon, von welchem eingebauten Konstruktor das Objekt erzeugt wurde. Die Prüfung, um welchen konkreten Fehlertyp es sich handelt, muss also auf andere Weise erfolgen.

Definition von Tags

Das Standardverhalten von Object.prototype.toString() ist es, eine Zeichenkette mit dem Aufbau "[object typename]" zurückzugeben. Für einige JavaScript-Typen (undefined, null, Array, Arguments, Error, Function, Boolean, Number, String, Date und RegExp) enthält die Methode eine Abfrage auf den genauen Typ und setzt ihn für typename ein. Für alle anderen Objekttypen schreibt sie lediglich "[object Object]".

Dieses Verhalten kann beeinflusst werden, indem man dem Objekt eine Eigenschaft mit dem Symbol @@toStringTag zuweist oder indem es die Eigenschaft von seinem Prototypen erbt. Das Symbol finden Sie in Symbol.toStringTag. Wenn Object.prototype.toString() keinen bekannten Typ vorfindet, aber die @@toStringTag-Eigenschaft, dann verwendet es deren Wert an Stelle von "Object". Einige neuere Datentypen wie Map oder Set verwenden diesen Weg.

Beispiel
class Test {
   construct(name) {
      this.name = name;
   }
   get [Symbol.toStringTag]() {          // Getter für die @@toStringTag-Eigenschaft
      return "Test";
   }
}

const t = new Test();
console.log(t.toString()); // Ausgabe: [object Test]

Das Beispiel definiert eine simple Klasse namens Test und darin einen Property-Getter für @@toStringTag, wodurch auf dem Prototypobjekt der Test-Klasse ein entsprechendes Property erzeugt wird. Die eckigen Klammern werden verwendet, wenn der Name einer Methode nicht als Literal notiert werden kann, sondern erst zur Laufzeit ermittelt wird- hier aus der Eigenschaft toStringTag von Symbol.

Die Deklaration als get-Property erzeugt auf dem Prototypen von Test eine entsprechende Eigenschaft, die als nicht aufzählbar markiert ist. Das entspricht dem Muster, nach dem auch die Systemklassen von JavaScript diese Eigenschaft bereitstellen.

Wird nun auf einem durch Test erzeugten Objekt die toString-Methode aufgerufen, so wird die von Object.prototype geerbte Methode verwendet. Diese verwendet die @@toStringTag-eigenschaft, um die gezeigte Darstellung zu erzeugen.

Beachten Sie: Die @@toStringTag-Eigenschaft muss eine Zeichenkette enthalten. Andere Werte werden von Object.prototype.toString() ignoriert.

An Stelle eines get-Propertys, das den Zusatzaufwand einer Funktion mit sich bringt, können Sie auch die defineProperty-Methode von Object verwenden, um auf Test.prototype die passende Eigenschaft zu erzeugen:

@@toStringTag mit defineProperty
class Test {
   // Methoden etc
}
Object.defineProperty(
     Test.prototype,
     Symbol.toStringTag, { configurable: true, writable: true, value: 'Test' });

Das ist zwar mehr Schreibarbeit, aber so ist @@toStringTag eine einfache Werteeigenschaft.

Spezifischere Methoden

Die Objektmethode toString wird wie eingangs bereits erwähnt, bei einigen eingebauten Objekten wie etwa Arrays entlang der Prototypenkette durch gleichnamige Methoden verschattet, welche eine alternative, an den jeweiligen Objekttyp angepasste Stringrepräsentation erzeugen, weshalb sie in diesen Fällen nicht direkt auf diesen Objekten aufgerufen werden kann, sondern nur über Methoden wie call oder apply.


Beispiel
const array = [16, 32, 64];

console.log(Object.prototype.toString.call(array)); // [object Array]

console.log(array.toString( )); // 16,32,64


Wird also zum Beispiel die Methode toString von Array.prototype aufgerufen, dann wird anders als bei der Methode von Object.prototype kein String erzeugt, der Informationen über den Typ enthält, sondern stattdessen eine Liste der Elemente des Arrays. In gleicher Weise wird etwa bei einem Aufruf der Methode toString von Boolean.prototype der String true oder false ausgegeben, abhängig davon welcher der beiden Werte in dem gegebenenfalls temporären spezifischen Objekt gekapselt wurde.

Eigene Eigenschaften

Die Methode toString besitzt zwei eigene Eigenschaften mit den Namen length und name. Beide Eigenschaften sind schreibgeschützt aber konfigurierbar, können also nicht durch Zuweisung, sondern nur durch ausdrückliche Definition verändert werden. Von einer solchen Veränderung ist allerdings dringend abzuraten, da die Manipulation von Standardobjekten grundsätzlich das Risiko unerwünschter Nebeneffekte birgt.


Beispiel
console.info({ }.toString.name); // toString

console.info({ }.toString.length); // 0


Die Eigenschaft name von Funktionen enthält einen String mit dem Namen der Funktion, sofern die diese benannt ist. Demnach wird hier die Zeichenkette toString zurückgegeben. Die Eigenschaft mit dem Namen length gibt bei Funktionen die Anzahl der formalen Parameter zurück. Da die Methode toString wie gesehen über keine Parameter verfügt, ist hier dem zur Folge als Wert die Zahl 0 hinterlegt.

Weblinks