2010-03-21 16 views
100

In Javascript hat jedes Objekt eine valueOf() und toString() Methode. Ich hätte gedacht, dass die toString() -Methode immer aufgerufen wird, wenn eine String-Konvertierung aufgerufen wird, aber anscheinend wird sie von valueOf() übertrumpft.valueOf() vs. toString() in Javascript

Zum Beispiel kann der Code

var x = {toString: function() {return "foo"; }, 
     valueOf: function() {return 42; }}; 
window.console.log ("x="+x); 
window.console.log ("x="+x.toString()); 

x=42 
x=foo 

Das erscheint mir als rückwärts gedruckt wird .. wenn x eine komplexe Zahl waren zum Beispiel würde ich valueOf wollen() geben mir seine Größe, aber wann immer ich in eine Zeichenfolge konvertieren wollte, würde ich etwas wie "a + bi" wollen. Und ich möchte toString() nicht explizit in Kontexten aufrufen müssen, die eine Zeichenkette enthalten.

Ist das genau so?

+5

Haben Sie 'window.console.log (x);' oder 'alert (x);'? – Li0liQ

+4

Sie geben "Object" und "Foo" jeweils. Lustige Sachen. – brainjam

+0

Eigentlich alert (x); gibt "foo" und window.console.log (x); gibt "foo {}" in Firebug und das gesamte Objekt in der Chrome-Konsole an. – brainjam

Antwort

95

Der Grund warum ("x =" + x) gibt "x = value" und nicht "x = tostring " ist das Folgende. Bei der Bewertung von "+" sammelt javascript zunächst Grundwerte der Operanden und entscheidet dann, ob eine Addition oder Verkettung angewendet werden soll, basierend auf dem Typ jedes Grundelements.

Also, das ist, wie Sie denken, es

a + b: 
    pa = ToPrimitive(a) 
    if(pa is string) 
     return concat(pa, ToString(b)) 
    else 
     return add(pa, ToNumber(b)) 

arbeitet und das ist, was passiert eigentlich

a + b: 
    pa = ToPrimitive(a) 
    pb = ToPrimitive(b)* 
    if(pa is string || pb is string) 
     return concat(ToString(pa), ToString(pb)) 
    else 
     return add(ToNumber(pa), ToNumber(pb)) 

Das heißt, toString auf das Ergebnis der valueOf angewendet wird, nicht zu Ihrem ursprünglichen Objekt .

Weitere Referenz finden Sie in Abschnitt 11.6.1 The Addition operator (+) in der ECMAScript-Sprachspezifikation.


* Wenn in String-Kontext genannt, ToPrimitive tut invoke toString, aber das ist hier nicht der Fall, weil '+' keine Art Kontext erzwingen.

+2

Sollte die Bedingung im Block "tatsachlich" nicht lesen "if (pa ist string && pb ist string)"? I.e "&&" statt "||" ? – brainjam

+3

Der Standard sagt definitiv "oder" (siehe den Link). – user187291

+0

Ja, Sie haben Recht. – brainjam

64

Hier ist ein wenig mehr Details, bevor ich auf die Antwort erhalten:

var x = { 
    toString: function() { return "foo"; }, 
    valueOf: function() { return 42; } 
}; 

alert(x); // foo 
"x=" + x; // "x=42" 
x + "=x"; // "42=x" 
x + "1"; // 421 
x + 1; // 43 
["x=", x].join(""); // "x=foo" 

Die toString Funktion ist nicht „erfundene“ von valueOf im Allgemeinen. Der ECMAScript-Standard beantwortet diese Frage sehr gut. Jedes Objekt verfügt über eine Eigenschaft [[DefaultValue]], die bei Bedarf berechnet wird. Wenn Sie nach dieser Eigenschaft fragen, gibt der Interpreter auch einen "Hinweis" für die Art des erwarteten Werts. Wenn der Hinweis String lautet, wird toString vor valueOf verwendet. Wenn der Hinweis jedoch Number lautet, wird zuerst valueOf verwendet. Beachten Sie, dass, wenn nur einer vorhanden ist oder ein nicht-primitives Objekt zurückgibt, es das andere als zweite Wahl bezeichnet. Der Operator + liefert immer den Hinweis Number, auch wenn der erste Operand ein Zeichenfolgenwert ist. Auch wenn es x für seine Number Darstellung fragt, da der erste Operand eine Zeichenfolge aus [[DefaultValue]] zurückgibt, führt es String-Verkettung.

Wenn Sie sicherstellen möchten, dass toString für die Verkettung von Zeichenfolgen aufgerufen wird, verwenden Sie ein Array und die Methode .join("").

(Actionscript 3.0 ändert etwas, das Verhalten von + jedoch. Wenn einer der Operanden ein String ist, es als String-Verkettungsoperator behandeln und verwendet den Hinweis String wenn es [[DefaultValue]] nennt. Also, in AS3, dieses Beispiel ergibt "foo, x = foo, foo = x, foo1, 43, x = foo".)

+0

Beachten Sie auch, dass wenn "valueOf" oder "toString" Nicht-Primitive zurückgibt, diese ignoriert werden. Wenn keiner existiert oder keiner von ihnen ein Grundelement zurückgibt, wird ein 'TypeError' geworfen. – bcherry

+1

Dank bcherry, das ist das Kaliber der Antwort, auf die ich gehofft hatte. Aber sollte nicht x + "x ="; Ausbeute "42x ="? Und x + "1"; Ertrag 421? Haben Sie auch eine URL für den relevanten Teil des ECMAScript-Standards? – brainjam

+0

Hoppla, ja. Ein Tippfehler und ein Fehler beim Testen von "1" + 1 ". Ich habe den ursprünglichen Post bearbeitet. Danke :) Hier ist die ECMAScript 3 Spezifikation (pdf): http://www.mozilla.org/js/language/E262-3.pdf Sie suchen nach '8.6.2.6 [[DefaultValue]] (Hinweis) ', die auf Seite 35 ist. – bcherry