javascript - Ce înseamnă că `toString` este „ignorată” dacă returnează ceva non-primar?
Documentația MDN despre Object.prototype.toString spune că atunci când toString este suprascris, ar trebui să returneze doar o valoare primitivă:
Funcția
toString()pe care o creezi trebuie să returneze un tip primitiv, altfel va fi ignorată.
Totuși, în exemplul următor returnăm un obiect în interiorul toString și acesta returnează obiectul în mod normal:
const ob2 = {
val1: 100,
val2: 200,
toString: function() {
return {
veh: "meh"
}; // Object.
}
};
console.log(ob2.toString());Ieșire :
{ veh: "meh" }
Deci metoda toString returnează obiectul în mod normal. Ce se întâmplă cu dezinformarea?
Răspuns #1
Ai dreptate, documentația a fost înșelătoare și incompletă. Am trimis o cerere de pull care o reformulează după cum urmează:
Această parte a fost eliminată:
FuncțiatoString()pe care o creezi trebuie să returneze un primitiv, altfel va fi ignorată.
Înlocuit de:
Funcția
toString()pe care o creezi trebuie să returneze un tip primitiv. Dacă returnează un obiect și metoda este apelată implicit (adică în timpul conversiei sau forțării tipului), atunci rezultatul său va fi ignorat și va fi folosită valoarea unei metode corelate,valueOf(), sau va fi aruncată oTypeErrordacă niciuna dintre aceste metode nu returnează un tip primitiv.
Am găsit cererea de pull originală și commit-ul care a adăugat această formulare. Există un comentariu de revizuire al autorului referitor la această propoziție care spune:
Vezi pasul 5.B.ii din https://262.ecma-international.org/9.0/#sec-ordinarytoprimitive
La ce se referea autorul este consecința operațiunii abstracte OrdinaryToPrimitive din specificație: lăsând deoparte Symbol.toPrimitive, atunci când o valoare este transformată într-un primitiv, cele două metode toString și valueOf (numele metodelor methodNames) sunt pregătite să fie apelate într-o anumită ordine bazată pe un indiciu de tip. Și apoi:
Pentru fiecare element name din methodNames, fă
- Fie method ? Get(O, name).
Dacă IsCallable(method) este true, atunci
- Să fie result ? Call(method, O).
- Dacă Type(result) nu este Object, returnează result.
Acest pas este un ciclu, iterând peste lista de methodNames. Ia următoarea method din această listă, verifică dacă este o funcție, o apelează și stochează rezultatul în result. Apoi efectuează verificarea tipului. Dacă rezultatul este un primitiv, adică nu este un obiect, acest rezultat este returnat. În caz contrar, ciclul continuă, ignorând efectiv rezultatul.
Dacă bucla ajunge la sfârșit fără a returna o valoare, va fi aruncată o eroare de tip TypeError.
Pentru a demonstra acest comportament, trebuie să aveți ambele metode:
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.Ceea ce documentația nu menționa era apelarea explicită a metodei toString, deoarece asta funcționează foarte bine:
class Test {
toString() {
return {
hello: "world"
};
}
}
const test = new Test;
console.log(test.toString()); // Logs { hello: "world" }.