javascript - ¿Qué significa que `toString` se "ignore" si devuelve un valor no primitivo?
La documentación de MDN sobre Object.prototype.toString dice que cuando toString se sobrescribe, solo debe devolver un valor primitivo:
La función
toString()que crees debe devolver un primitivo, de lo contrario será ignorada.
Sin embargo, en el siguiente ejemplo devolvemos un objeto dentro de toString y devuelve el objeto normalmente:
const ob2 = {
val1: 100,
val2: 200,
toString: function() {
return {
veh: "meh"
}; // Object.
}
};
console.log(ob2.toString());Salida :
{ veh: "meh" }
Entonces, el método toString devuelve el objeto normalmente. ¿Qué pasa con la desinformación?
Respuesta #1
Tienes razón, la documentación era engañosa e incompleta. He enviado una solicitud de extracción que la reescribe de la siguiente manera:
Eliminada esta parte:
La funcióntoString()que crees debe devolver un primitivo, de lo contrario será ignorada.
Reemplazado por:
La función
toString()que crees debe devolver un primitivo. Si devuelve un objeto y el método se llama implícitamente (es decir, durante la conversión o coerción de tipo), entonces su resultado será ignorado y se usará el valor de un método relacionado,valueOf(), o se lanzará unTypeErrorsi ninguno de estos métodos devuelve un primitivo.
He encontrado la solicitud de extracción original y el commit que añadieron esta redacción. Hay un comentario de revisión del autor en referencia a esta frase que dice:
Véase el paso 5.B.ii en https://262.ecma-international.org/9.0/#sec-ordinarytoprimitive
A lo que el autor se refería es a la consecuencia de la operación abstracta OrdinaryToPrimitive en la especificación: dejando de lado Symbol.toPrimitive, cuando un valor se convierte en un primitivo, los dos métodos toString y valueOf (los methodNames) están preparados para ser llamados en un orden específico basado en una pista de tipo. Y luego:
Para cada elemento name de methodNames, hacer
- Sea method ? Get(O, name).
Si IsCallable(método) es verdadero, entonces
- Sea result=? Llamar(method, O).
- Si Type(resultado) no es Objeto, devuelve resultado.
Este paso es un bucle, iterando sobre la lista de methodNames. Toma el siguiente method de esta lista, verifica si es una función, la llama y almacena su resultado en result. Luego realiza la comprobación de tipo. Si el resultado es un primitivo, es decir, no un objeto, se devuelve este resultado. De lo contrario, el bucle continúa, ignorando efectivamente el resultado.
Si el bucle llega al final sin devolver un valor, se lanzará un TypeError.
Para demostrar este comportamiento, debes tener ambos métodos:
class Test1 {
toString() {
return "1";
}
valueOf() {
return "2";
}
}
class Test2 {
toString() {
return {};
}
valueOf() {
return "2";
}
}
class Test3 {
toString() {
return "1";
}
valueOf() {
return {};
}
}
class Test4 {
toString() {
return {};
}
valueOf() {
return {};
}
}
const test1 = new Test1,
test2 = new Test2,
test3 = new Test3,
test4 = new Test4;
console.log(String(test1)); // "1"; toString is preferred.
// `"" + test1` and `+test1` also demonstrate this.
console.log(Number(test1)); // 2; valueOf is preferred.
console.log(String(test2)); // "2"; toString is ignored; valueOf is chosen instead.
console.log(Number(test2)); // 2; valueOf is preferred.
console.log(String(test3)); // "1"; toString is preferred.
console.log(Number(test3)); // 1; valueOf is ignored; toString is chosen instead.
console.log(String(test4)); // TypeError; none of the methods returns a primitive.
console.log(Number(test4)); // TypeError; none of the methods returns a primitive.Lo que la documentación no mencionaba era llamar explícitamente al método toString, porque eso funciona perfectamente:
class Test {
toString() {
return {
hello: "world"
};
}
}
const test = new Test;
console.log(test.toString()); // Logs { hello: "world" }.