Hallo Zonk,
Wieso wird bei a. o1 nicht mit toString in eine Zeichenkette umgewandelt, in b. aber schon? alert("30" + 30) wandelt doch auch 30 in die Zeichenkette "30" mit dem Ergebnis 3030 um.
Die Krux liegt darin, wie der Ausdruck ausgewertet wird, daraus folgt die Typumwandlung.
alert("Das Objekt " + o1);
"Das Objekt 1"
Hier befinden wir uns in einem Additions-Ausdruck. Dieser ist im ECMAScript-Standard so definiert (Kommentare von mir):
The production AdditiveExpression : AdditiveExpression +
MultiplicativeExpression is evaluated as follows:
Produktion heisst hier soviel wie „Muster im Quelltext“. Lass Dich nicht von „MultiplicativeExpression“ irritieren, das kann auch einzelne Objekte identifizieren.
1. Evaluate AdditiveExpression.
2. Call GetValue(Result(1)).
3. Evaluate MultiplicativeExpression.
4. Call GetValue(Result(3)).
Das dient nur dazu, die Ausdrücke rekursiv auf einzelne Werte zusammenzudampfen und mit der ECMAScript-internen (also nicht nach aussen sichtbaren) Funktion GetValue() das tatsächliche Objekt bzw den tatsächlichen Wert statt nur einer Referenz auf diesen zu bekommen.
5. Call ToPrimitive(Result(2)).
6. Call ToPrimitive(Result(4)).
Das wird hier gleich wichtig.
7. If Type(Result(5)) is String or Type(Result(6)) is String, go to step 12.
Hier werden, wenn nur einer der beiden Werte vom Typ String ist, die gesamten Zahlenadditionsschritte übersprungen. Mache ich auch mal.
...
12. Call ToString(Result(5)).
13. Call ToString(Result(6)).
14. Concatenate Result(12) followed by Result(13).
15. Return Result(14).
Hier werden die Strings noch mal explizit in String-Primitives umgewandelt (klingt absurd, ich weiss), zusammengehängt und zurückgegeben.
Wichtig ist noch diese Notiz:
NOTE
No hint is provided in the calls to ToPrimitive in steps 5 and 6.
Wofür ist dieser Aufruf der wiederum internen Funktion ToPrimitive() notwendig? Der Additionsoperator will explizit mit richtigen Werten arbeiten – ToPrimitive ist also dazu zuständig etwaige Objekte in primitive Werte umzuwandeln. Es ist im wesentlichen als Tabelle spezifiziert, normale Werte bleiben gleich, nur für Objekte kriegen einen leicht kompliziertere Behandlung:
Object – Return a default value for the Object. The default value of an object
is retrieved by calling the internal [[DefaultValue]] method of the object,
passing the optional hint PreferredType. The behaviour of the [[DefaultValue]]
method is defined by this specification for all native ECMAScript objects.
Neben internen Funktionen haben Objekte auch noch interne Methoden. [[DefaultValue]] ist dazu zuständig einen konkreten Wert zu kreieren. Entweder als String mit toString() oder als „Wert“mit valueOf(). Zusätzlich kriegt diese interne Methode noch ein optionales Argument PreferredType als Vorschlag übergeben, mögliche Werte sind String und Number. ToPrimitive kriegt diesen möglichen Hint auch übergeben und gibt den auch weiter. Die nervige Schritt-für-Schritt-Beschreibung mal zusammengefasst:
• Wenn der Hint „String“ ist:
1. Versuch toString() aufzurufen. Wenn das einen primitiven Wert zurückgibt,
gib diesen zurück.
2. Versuch valueOf() aufzurufen. Wenn das einen primitiven Wert zurückgibt,
gib diesen zurück.
3. Gib auf und werfe einen TypeError.
• Wenn der Hint „Number“ ist:
1. Versuch valueOf() aufzurufen. Wenn das einen primitiven Wert zurückgibt,
gib diesen zurück.
2. Versuch toString() aufzurufen. Wenn das einen primitiven Wert zurückgibt,
gib diesen zurück.
3. Gib auf und werfe einen TypeError.
Und was ist, wenn kein Hint da ist?
When the [[DefaultValue]] method of O is called with no hint, then it behaves
as if the hint were Number, unless O is a Date object (see 15.9), in which
case it behaves as if the hint were String.
In unserem Fall sieht die interne Abfolge also so aus:
1. "Objekt " + o1 ruft intern ToPrimitive(o1) auf.
2. Das ruft o1.[[DefaultValue]] auf.
3. Dieses gibt den Wert von o1.valueOf() zurück - Dein "1"-String.
Warum setzt der Additionsausdruck keinen Hint? Vermutlich, weil er für beides, Addition und Stringkonkatention zuständig ist.
Warum ist der Hint „Number“ default? Ich habe keine Ahnung. Ich vermute höchstens, dass toString() zu speziell für die meisten Anwendungen von ToPrimitive ist.
b. alert(o1);
b. Wie ich denke "o"
Was passiert dagegen hier? alert() ist nicht von ECMAScript spezifiziert, deswegen kann ich nur raten. Allerdings ist es offensichtlich, dass alert() den enthaltenen Ausdruck in einen String umwandeln muss. Dazu verwendet es wohl die interne Funktion ToString und hier wird für Objekte als Argument als Hint beim Aufruf von ToPrimitive explizit der Hint String gesetzt:
Apply the following steps:
1. Call ToPrimitive(input argument, hint String).
2. Call ToString(Result(1)).
3. Return Result(2).
In einem expliziten String-Kontext wird also durchaus toString() aufgerufen.
c. alert(o1 + o1);
c. Ich erwarte 2: trifft zu.
Hm? In welchem Browser hast Du getestet? In Spidermonkey, dem Mozilla-JS-Interpreter, kriege ich als Ausgabe "11" wie es auch die Spezifikation vermuten lässt. Oder hast Du oben einen Tippfehler gemacht und Dein o.prototype.valueOf() soll gar keinen String ("1") zurückgeben, sondern eine Zahl (1)?
Tim