2010-03-21 15 views
100

In Javascript ogni oggetto ha un metodo valueOf() e toString(). Avrei pensato che il metodo toString() è stato richiamato ogni volta che viene richiesta una conversione di stringhe, ma a quanto pare viene trumpato da valueOf().valueOf() vs. toString() in Javascript

Ad esempio, il codice

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

stamperà

x=42 
x=foo 

Questo mi colpisce come all'indietro .. se x fosse un numero complesso, per esempio, vorrei valueOf() per dare me la sua grandezza, ma ogni volta che volevo convertire in una stringa vorrei qualcosa come "a + bi". E non vorrei dover chiamare toString() esplicitamente in contesti che implicavano una stringa.

È così?

+5

Avete provato 'window.console.log (x);' o 'alert (x);'? – Li0liQ

+4

Essi danno rispettivamente "Object" e "foo". Cose divertenti. – brainjam

+0

In realtà, avviso (x); dà "pippo" e window.console.log (x); fornisce "foo {}" in Firebug e l'intero oggetto nella console di Chrome. – brainjam

risposta

95

Il motivo per cui ("x =" + x) restituisce "x = valore" e non "x = tostring" "è il seguente. Quando si valuta "+", javascript prima raccoglie i valori primitivi degli operandi e quindi decide se applicare o aggiungere o concatenare, in base al tipo di ciascuna primitiva.

Quindi, questo è come si pensa funziona

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

e questo è ciò che accade in realtà

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)) 

Cioè, toString viene applicata al risultato di valueOf, non l'oggetto originale .

Per ulteriori informazioni, consultare la sezione 11.6.1 The Addition operator (+) nella specifica del linguaggio ECMAScript.


* Quando viene chiamato in contesto stringa, ToPrimitive fa invoke toString, ma questo non è il caso qui, perche '+' non far rispettare qualsiasi contesto tipo.

+2

Non dovrebbe leggere il condizionale nel blocco "in realtà" se (pa è stringa && pb è stringa) "? I.e "&&" invece di "||" ? – brainjam

+3

Lo standard dice decisamente "o" (vedi il link). – user187291

+0

Sì, hai ragione. – brainjam

64

Ecco un po 'più in dettaglio, prima di arrivare alla risposta:

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" 

La funzione toString è non "inventate" di valueOf in generale. Lo standard ECMAScript risponde molto bene a questa domanda. Ogni oggetto ha una proprietà [[DefaultValue]], che viene calcolata su richiesta. Quando si chiede questa proprietà, l'interprete fornisce anche un "suggerimento" per quale tipo di valore si aspetta. Se il suggerimento è String, allora toString viene utilizzato prima dello valueOf. Tuttavia, se il suggerimento è Number, verrà utilizzato prima valueOf. Nota che se solo uno è presente, o restituisce un non-primitivo, di solito chiamerà l'altro come seconda scelta.

L'operatore + fornisce sempre il suggerimento Number, anche se il primo operando è un valore stringa. Anche se richiede x per la sua rappresentazione Number, poiché il primo operando restituisce una stringa da [[DefaultValue]], esegue la concatenazione di stringhe.

Se si desidera garantire che toString venga chiamato per la concatenazione di stringhe, utilizzare un array e il metodo .join("").

(ActionScript 3.0 modifica leggermente il comportamento di +, tuttavia. Se l'operando è un String, verrà trattato come un operatore di concatenazione di stringhe e utilizzare l'indizio String quando chiama [[DefaultValue]]. Così, in AS3, questo esempio produce "pippo, x = pippo, pippo = x, pippo1, 43, x = pippo".)

+0

Si noti inoltre che se 'valueOf' o' toString' restituiscono non primitivi, vengono ignorati. Se nessuno dei due esiste, o nessuno dei due restituisce una primitiva, viene lanciato un 'TypeError'. – bcherry

+1

Grazie bcherry, questo è il calibro della risposta che speravo. Ma non dovrebbe x + "x ="; resa "42x ="? E x + "1"; cedere 421? Inoltre, si dispone di un URL per la parte rilevante dello standard ECMAScript? – brainjam

+0

Oops, sì. Un errore di battitura e un errore dal test '' 1 "+ 1', rispettivamente. Ho modificato il post originale. Grazie :) Ecco la specifica ECMAScript 3 (pdf): http://www.mozilla.org/js/language/E262-3.pdf Stai cercando '8.6.2.6 [[DefaultValue]] (suggerimento) ', che si trova a pagina 35. – bcherry