En Javascript, cada objeto tiene un método valueOf () y toString (). Pensé que el método toString () se invocaba cada vez que se solicitaba una conversión de cadena, pero aparentemente valueOf () lo supera.
Por ejemplo, el código
var x = {toString: function() {return "foo"; },
valueOf: function() {return 42; }};
window.console.log ("x="+x);
window.console.log ("x="+x.toString());
imprimirá
x=42
x=foo
Esto me parece al revés ... si x fuera un número complejo, por ejemplo, me gustaría que valueOf () me diera su magnitud, pero siempre que quisiera convertir a una cadena, querría algo como "a + bi". Y no me gustaría tener que llamar a toString () explícitamente en contextos que implican una cadena.
¿Es así como es?
javascript
cerebro atascado
fuente
fuente
window.console.log (x);
oalert (x);
?alert(x)
muestrafoo
ywindow.console.log(x)
muestraObject { toString: x.toString(), valueOf: x.valueOf() }
.Respuestas:
La razón por la que ("x =" + x) da "x = valor" y no "x = una cadena" es la siguiente. Al evaluar "+", JavaScript primero recopila los valores primitivos de los operandos y luego decide si se debe aplicar la suma o la concatenación, en función del tipo de cada primitiva.
Entonces, así es como crees que funciona
y esto es lo que realmente pasa
Es decir, toString se aplica al resultado de valueOf, no a su objeto original.
Para obtener más información, consulte la sección 11.6.1 El operador de suma (+) en la Especificación del lenguaje ECMAScript.
* Cuando se llama en un contexto de cadenas, ToPrimitive hace invocar toString, pero este no es el caso aquí, porque '+' no cumplir cualquier tipo de contexto.
fuente
Aquí hay un poco más de detalle, antes de llegar a la respuesta:
La
toString
función no es "superada"valueOf
en general. El estándar ECMAScript realmente responde bastante bien a esta pregunta. Cada objeto tiene una[[DefaultValue]]
propiedad, que se calcula bajo demanda. Al preguntar por esta propiedad, el intérprete también proporciona una "pista" sobre el tipo de valor que espera. Si la pista esString
, entoncestoString
se usa antesvalueOf
. Pero, si la sugerencia esNumber
, entoncesvalueOf
se usará primero. Tenga en cuenta que si solo está presente uno, o devuelve un no primitivo, normalmente llamará al otro como segunda opción.El
+
operador siempre proporciona la pistaNumber
, incluso si el primer operando es un valor de cadena. Aunque solicitax
suNumber
representación, dado que el primer operando devuelve una cadena de[[DefaultValue]]
, realiza una concatenación de cadenas.Si desea garantizar que
toString
se llame para la concatenación de cadenas, use una matriz y el.join("")
método.(
+
Sin embargo, ActionScript 3.0 modifica ligeramente el comportamiento de . Si cualquiera de los operandos es aString
, lo tratará como un operador de concatenación de cadenas y usará la sugerenciaString
cuando llame[[DefaultValue]]
. Por lo tanto, en AS3, este ejemplo produce "foo, x = foo, foo = x, foo1, 43, x = foo ".)fuente
valueOf
otoString
devuelve no primitivos, se ignoran. Si ninguno existe, o ninguno devuelve un primitivo,TypeError
se lanza a.[[DefaultValue]](no-hint)
, que es equivalente a[[DefaultValue]](number)
.("" + new Date(0)) === new Date(0).toString()
. Un objeto Date siempre parece devolver sutoString()
valor cuando se agrega a algo.TLDR
La coerción de tipos, o conversión de tipos implícita, permite una escritura débil y se usa en todo JavaScript. La mayoría de los operadores (con la notable excepción de los operadores de igualdad estricta
===
y!==
), y las operaciones de verificación de valor (por ejemploif(value)...
), forzarán los valores que se les proporcionen, si los tipos de esos valores no son inmediatamente compatibles con la operación.El mecanismo preciso utilizado para coaccionar un valor depende de la expresión que se evalúa. En la pregunta, se está utilizando el operador de suma .
El operador de suma primero se asegurará de que ambos operandos sean primitivos, lo que, en este caso, implica llamar al
valueOf
método. EltoString
método no se llama en esta instancia porque elvalueOf
método reemplazado en el objetox
devuelve un valor primitivo.Entonces, debido a que uno de los operandos en la pregunta es una cadena, ambos operandos se convierten en cadenas. Este proceso utiliza la operación interna abstracta
ToString
(nota: en mayúscula) y es distinto deltoString
método en el objeto (o su cadena prototipo).Finalmente, las cadenas resultantes se concatenan.
Detalles
En el prototipo de cada objeto de función de constructor correspondiente a cada tipo de lenguaje en JavaScript (es decir, Número, BigInt, Cadena, Booleano, Símbolo y Objeto), hay dos métodos:
valueOf
ytoString
.El propósito de
valueOf
es recuperar el valor primitivo asociado con un objeto (si tiene uno). Si un objeto no tiene un valor primitivo subyacente, simplemente se devuelve el objeto.Si
valueOf
se invoca contra una primitiva, la primitiva se encuadra automáticamente de la forma normal y se devuelve el valor de la primitiva subyacente. Tenga en cuenta que para las cadenas, el valor primitivo subyacente (es decir, el valor devuelto porvalueOf
) es la propia representación de la cadena.El siguiente código muestra que el
valueOf
método devuelve el valor primitivo subyacente de un objeto contenedor, y muestra cómo las instancias de objetos no modificados que no corresponden a primitivas, no tienen ningún valor primitivo para devolver, por lo que simplemente regresan a sí mismas.El propósito de
toString
, por otro lado, es devolver una representación de cadena de un objeto.Por ejemplo:
Para la mayoría de las operaciones, JavaScript intentará convertir silenciosamente uno o más operandos al tipo requerido. Este comportamiento se eligió para facilitar el uso de JavaScript. JavaScript inicialmente no tenía excepciones , y esto también puede haber jugado un papel en esta decisión de diseño. Este tipo de conversión de tipo implícita se denomina coerción de tipo y es la base del sistema de tipo flexible (débil) de JavaScript. Las complicadas reglas detrás de este comportamiento están destinadas a trasladar la complejidad del encasillamiento al lenguaje mismo y fuera de su código.
Durante el proceso coercitivo, hay dos modos de conversión que pueden ocurrir:
Number()
,Boolean()
,String()
Etc.)Conversión a un primitivo
Cuando se intenta convertir tipos no primitivos en primitivos sobre los que operar, la operación abstracta
ToPrimitive
se llama con una "sugerencia" opcional de 'número' o 'cadena'. Si se omite la sugerencia, la sugerencia predeterminada es 'número' (a menos que el@@toPrimitive
método haya sido anulado). Si la pista es 'cadena', entoncestoString
se intenta primero, yvalueOf
segundo sitoString
no devolvió una primitiva. De lo contrario, viceversa. La sugerencia depende de la operación que solicita la conversión.El operador de adición no proporciona ninguna pista, por lo que
valueOf
se intenta primero. El operador de resta proporciona una pista de 'número', por lo quevalueOf
se intenta primero. Las únicas situaciones que puedo encontrar en la especificación en las que la pista es 'cadena' son:Object#toString
ToPropertyKey
, que convierte un argumento en un valor que se puede utilizar como clave de propiedad.Conversión de tipo directo
Cada operador tiene sus propias reglas para completar su operación. El operador de suma se utilizará primero
ToPrimitive
para garantizar que cada operando sea una primitiva; luego, si cualquiera de los operandos es una cadena, entonces invocará deliberadamente la operación abstractaToString
en cada operando, para entregar el comportamiento de concatenación de cadenas que esperamos con cadenas. Si, después delToPrimitive
paso, ambos operandos no son cadenas, se realiza la suma aritmética.A diferencia de la suma, el operador de resta no tiene un comportamiento sobrecargado, por lo que invocará
toNumeric
en cada operando habiéndolos convertido primero en primitivas usandoToPrimitive
.Entonces:
Tenga en cuenta que el
Date
objeto intrínseco es único, ya que es el único intrínseco que anula el@@toPrimitive
método predeterminado , en el que se presume que la sugerencia predeterminada es 'cadena' (en lugar de 'número'). La razón para tener esto es que lasDate
instancias se traduzcan a cadenas legibles por defecto, en lugar de su valor numérico, para la conveniencia del programador. Puede anular@@toPrimitive
sus propios objetos usandoSymbol.toPrimitive
.La siguiente cuadrícula muestra los resultados de coerción para el operador de igualdad abstracto (
==
) ( fuente ):Vea también .
fuente