W3docs

Conversión de objetos a primitivos en JavaScript

Aprende cómo JavaScript convierte objetos a primitivos: las sugerencias string, number y default, el método Symbol.toPrimitive y la cadena de respaldo toString/valueOf.

Introducción a la conversión de objetos a primitivos

En JavaScript, los objetos son valores de referencia, pero muchas operaciones esperan un primitivo (un string, un número o un boolean). Cuando escribes obj + "", +obj o `${obj}`, el lenguaje debe convertir primero el object en un primitivo antes de ejecutar la operación. Esto se denomina conversión de objeto a primitivo.

Esta guía explica las reglas que sigue JavaScript: las tres sugerencias de conversión ("string", "number", "default"), el método Symbol.toPrimitive que te permite controlar la conversión, y la cadena de respaldo toString()/valueOf() que se usa cuando Symbol.toPrimitive no está presente.

Cómo funciona la conversión de objeto a primitivo

No existe ningún operador que convierta un object a un boolean: los objetos son siempre verdaderos en un contexto boolean. Por tanto, la conversión de objeto a primitivo solo produce un string o un número, y JavaScript decide cuál usar pasándole al object una sugerencia:

  1. Primero busca un método [Symbol.toPrimitive](hint). Si está presente, se llama y se usa su valor de retorno (que debe ser un primitivo).
  2. Si Symbol.toPrimitive no existe, JavaScript recurre a toString() y valueOf(), llamándolos en un orden que depende de la sugerencia.

Veremos el respaldo en detalle más adelante. Primero, el enfoque moderno y explícito.

Ejemplo: implementar Symbol.toPrimitive

javascript— editable

Explicación: El object user define un único método Symbol.toPrimitive que se ramifica según la sugerencia. Un literal de plantilla solicita la sugerencia "string", la multiplicación solicita "number", y el operador binario + solicita "default". Devolver this.money para el caso predeterminado mantiene la coherencia aritmética con + y *.

Comprender las sugerencias de conversión

Una sugerencia es un string que el motor pasa para indicar al object qué tipo de primitivo prefiere la operación:

  • "string": se espera que el resultado sea un string — String(obj), `${obj}`, alert(obj), o un object usado como clave de propiedad.
  • "number": se espera un resultado numérico — +obj unario, obj * 2, obj - 1, obj < other, Number(obj), Math.round(obj).
  • "default": el operador acepta cualquier tipo y no sabe cuál solicitar. Es menos frecuente de lo que se piensa, pero importa: el operador binario + (que puede significar suma y concatenación de strings) usa "default", al igual que los operadores de igualdad débil == / != al comparar un object con un número o string.

Una sorpresa habitual: obj + "" no usa la sugerencia "string" — usa "default". Si solo manejas "string" y "number", la rama "default" es la que se ejecuta con +.

Ejemplo: gestionar diferentes sugerencias

javascript— editable

Explicación: Aquí el object item gestiona las tres sugerencias. Observa la última línea: como el operador binario + usa la sugerencia "default", item + '' ejecuta la rama "default" — no la rama "string" — produciendo "Item: Chair, Price: 45". Esta es exactamente la sutileza que hace que valga la pena manejar cada sugerencia de forma explícita. Consulta también operadores de comparación y operadores numéricos.

El respaldo toString / valueOf

Si un object no tiene el método Symbol.toPrimitive, JavaScript utiliza el par de métodos más antiguo y elige un orden basado en la sugerencia:

  • Para la sugerencia "string": se intenta primero toString(), luego valueOf().
  • Para la sugerencia "number" o "default": se intenta primero valueOf(), luego toString().

En cada caso se usa el primer método que devuelve un primitivo; si un método devuelve un object, se omite y se prueba el siguiente. Un object simple hereda Object.prototype.toString (que devuelve "[object Object]") y Object.prototype.valueOf (que devuelve el propio object, por lo que se ignora) — por eso ({}) + "" es "[object Object]".

javascript— editable

Explicación: Sin Symbol.toPrimitive, la sugerencia "string" llega a toString() y devuelve "John", mientras que las sugerencias numéricas y predeterminadas llegan a valueOf() y devuelven 1000. Symbol.toPrimitive es preferible en código nuevo porque proporciona un único lugar explícito para gestionar cada sugerencia; toString/valueOf siguen siendo útiles cuando solo te importa una dirección.

Buenas prácticas para usar toPrimitive

Implementar Symbol.toPrimitive de forma eficaz implica combinar claridad, coherencia y pruebas exhaustivas para garantizar que los objetos se comporten de manera predecible al convertirse a primitivos. A continuación se explica cómo aplicar estas buenas prácticas al usar el método Symbol.toPrimitive:

1. Semántica clara

Buena práctica: Define Symbol.toPrimitive de forma clara para que las conversiones del object sean predecibles y comprensibles. Esto implica gestionar explícitamente los diferentes tipos de sugerencias de conversión ("string", "number" y "default") y proporcionar valores de retorno adecuados para cada caso.

Ejemplo:

javascript— editable

Explicación: En este ejemplo, el object dateEvent define claramente los comportamientos de conversión para los contextos string y number. Para las conversiones a string devuelve una descripción, y para las conversiones a number devuelve la marca de tiempo del evento. Esta distinción clara ayuda a otros desarrolladores a entender qué esperar al convertir el object en diferentes contextos.

2. Coherencia

Buena práctica: Asegúrate de que las conversiones sean coherentes con los datos del object y su uso previsto, evitando comportamientos confusos o ilógicos.

javascript— editable

Explicación: El object product garantiza que la lógica de conversión sea coherente con sus propiedades. Ya sea para convertirlo a string para mostrarlo o a number para cálculos, el resultado sigue siendo intuitivo y útil, respetando el uso previsto de cada propiedad.

3. Pruebas

Buena práctica: Prueba exhaustivamente cómo se comportan tus objetos en distintos escenarios de conversión para evitar errores inesperados en tu aplicación.

Enfoques de prueba de ejemplo:

  • Pruebas unitarias: Escribe pruebas unitarias que intenten convertir el object mediante distintas operaciones (como operaciones aritméticas, concatenación de strings o pasar el object a funciones que esperan un tipo primitivo) para garantizar que todos los escenarios devuelven los valores esperados.
// Note: In a browser environment, use console.assert or a test framework like Jest/Mocha.
// Assumes 'product' is defined as in the previous example.
console.assert(String(product) === "Laptop costs $1200", "String conversion failed");
console.assert(+product === 1200, "Number conversion failed");
console.assert(product + '' === "Laptop", "Default conversion failed");

Explicación: Mediante pruebas unitarias, puedes verificar que el object product gestiona correctamente todas las formas de conversión según la lógica especificada en Symbol.toPrimitive. Esto ayuda a garantizar la fiabilidad y coherencia en cómo interactúa tu object con distintas partes del motor de JavaScript y tu aplicación.

Errores comunes

  • No existe sugerencia boolean. En un contexto boolean (if (obj), !obj, obj && x) el object es siempre verdadero y nunca se convierte a primitivo. La conversión de objeto a primitivo solo produce strings y números.
  • + usa "default", no "string". Esto confunde a muchos desarrolladores: obj + "" activa la sugerencia predeterminada. Las comparaciones como obj == 5 también usan "default".
  • Un método debe devolver un primitivo. Si Symbol.toPrimitive (o valueOf/toString) devuelve un object en lugar de un primitivo, se produce un TypeError. En el par de respaldo, devolver un object simplemente hace que ese método se omita.
  • La conversión numérica de un resultado string puede producir NaN. Si tu rama "number"/"default" devuelve un string no numérico, los contextos que esperan un número obtienen NaN: +{ [Symbol.toPrimitive]: () => "abc" } es NaN.

Conclusión

La conversión de objeto a primitivo es un mecanismo fundamental de JavaScript que permite a los objetos participar en operaciones aritméticas, concatenación de strings y comparaciones. El motor elige una sugerencia ("string", "number" o "default"), intenta primero Symbol.toPrimitive y, en caso contrario, recurre a toString()/valueOf(). Al implementar Symbol.toPrimitive, obtienes un único lugar explícito para controlar cómo se comporta un object personalizado en cada contexto, lo que conduce a un código más predecible y mantenible. Para profundizar, revisa tipos de datos, tipos symbol y métodos de object y this.

Práctica

Práctica
Al evaluar obj + '' y obj tiene un método Symbol.toPrimitive, ¿qué sugerencia pasa JavaScript?
Al evaluar obj + '' y obj tiene un método Symbol.toPrimitive, ¿qué sugerencia pasa JavaScript?
Práctica
Si un object no tiene el método Symbol.toPrimitive, ¿en qué orden intenta JavaScript para la sugerencia 'number'?
Si un object no tiene el método Symbol.toPrimitive, ¿en qué orden intenta JavaScript para la sugerencia 'number'?
Was this page helpful?