TLDR
La coercizione del tipo, o conversione del tipo implicita, consente una digitazione debole e viene utilizzata in JavaScript. La maggior parte degli operatori (con la notevole eccezione degli operatori di uguaglianza rigorosa ===e !==) e delle operazioni di controllo del valore (ad es. if(value)...), Forzeranno i valori forniti loro, se i tipi di quei valori non sono immediatamente compatibili con l'operazione.
Il meccanismo preciso utilizzato per forzare un valore dipende dall'espressione valutata. Nella domanda viene utilizzato l' operatore di addizione .
L'operatore di addizione assicurerà innanzitutto che entrambi gli operandi siano primitivi, il che, in questo caso, implica la chiamata del valueOfmetodo. Il toStringmetodo non viene chiamato in questa istanza perché il valueOfmetodo sottoposto a override sull'oggetto xrestituisce un valore primitivo.
Quindi, poiché uno degli operandi nella domanda è una stringa, entrambi gli operandi vengono convertiti in stringhe. Questo processo utilizza l'operazione astratta interna ToString(nota: maiuscola) ed è distinta dal toStringmetodo sull'oggetto (o dalla sua catena di prototipi).
Infine, le stringhe risultanti vengono concatenate.
Dettagli
Sul prototipo di ogni oggetto funzione del costruttore corrispondente a ogni tipo di linguaggio in JavaScript (ad esempio Number, BigInt, String, Boolean, Symbol e Object), ci sono due metodi: valueOfe toString.
Lo scopo di valueOfè recuperare il valore primitivo associato a un oggetto (se ne ha uno). Se un oggetto non ha un valore primitivo sottostante, l'oggetto viene semplicemente restituito.
Se valueOfviene invocato su una primitiva, la primitiva viene auto-boxed in modo normale e viene restituito il valore della primitiva sottostante. Notare che per le stringhe, il valore primitivo sottostante (cioè il valore restituito da valueOf) è la rappresentazione della stringa stessa.
Il codice seguente mostra che il valueOfmetodo restituisce il valore primitivo sottostante da un oggetto wrapper e mostra come le istanze di oggetti non modificate che non corrispondono alle primitive non hanno alcun valore primitivo da restituire, quindi restituiscono semplicemente se stesse.
console.log(typeof new Boolean(true)) // 'object'
console.log(typeof new Boolean(true).valueOf()) // 'boolean'
console.log(({}).valueOf()) // {} (no primitive value to return)
Lo scopo di toString, d'altra parte, è restituire una rappresentazione di stringa di un oggetto.
Per esempio:
console.log({}.toString()) // '[object Object]'
console.log(new Number(1).toString()) // '1'
Per la maggior parte delle operazioni, JavaScript tenterà silenziosamente di convertire uno o più operandi nel tipo richiesto. Questo comportamento è stato scelto per rendere JavaScript più facile da usare. JavaScript inizialmente non aveva eccezioni e anche questo potrebbe aver giocato un ruolo in questa decisione di progettazione. Questo tipo di conversione del tipo implicito è chiamato coercizione del tipo ed è la base del sistema di tipi libero (debole) di JavaScript. Le complicate regole alla base di questo comportamento hanno lo scopo di spostare la complessità del typecasting nel linguaggio stesso e fuori dal codice.
Durante il processo coercitivo, ci sono due modalità di conversione che possono verificarsi:
- Conversione di un oggetto in una primitiva (che potrebbe implicare una conversione di tipo stessa) e
- Conversione diretta a un'istanza tipo specifico, utilizzando un oggetto funzione di costruzione di uno dei tipi primitivi (es.
Number(), Boolean(), String()Etc.)
Conversione in un primitivo
Quando si tenta di convertire tipi non primitivi in primitive su cui operare, l'operazione astratta ToPrimitiveviene chiamata con un "suggerimento" opzionale di "numero" o "stringa". Se il suggerimento viene omesso, il suggerimento predefinito è "numero" (a meno che il @@toPrimitivemetodo non sia stato sovrascritto). Se il suggerimento è "stringa", toStringviene provato prima e valueOfsecondo se toStringnon ha restituito una primitiva. Altrimenti, viceversa. Il suggerimento dipende dall'operazione che richiede la conversione.
L'operatore di aggiunta non fornisce alcun suggerimento, quindi valueOfviene provato per primo. L'operatore di sottrazione fornisce un accenno di "numero", quindi valueOfviene provato per primo. Le uniche situazioni che riesco a trovare nella specifica in cui il suggerimento è "stringa" sono:
Object#toString
- L'operazione astratta
ToPropertyKey, che converte un argomento in un valore che può essere utilizzato come chiave di proprietà
Conversione diretta del tipo
Ogni operatore ha le proprie regole per completare la propria operazione. L'operatore di addizione utilizzerà innanzitutto ToPrimitiveper garantire che ogni operando sia primitivo; quindi, se uno degli operandi è una stringa, richiamerà deliberatamente l'operazione astratta ToStringsu ogni operando, per fornire il comportamento di concatenazione delle stringhe che ci aspettiamo con le stringhe. Se, dopo il ToPrimitivepassaggio, entrambi gli operandi non sono stringhe, viene eseguita l'addizione aritmetica.
A differenza dell'addizione, l'operatore di sottrazione non ha un comportamento di sovraccarico, quindi invocherà toNumericsu ogni operando dopo averli convertiti prima in primitive usando ToPrimitive.
Così:
1 + 1 // 2
'1' + 1 // '11' Both already primitives, RHS converted to string, '1' + '1', '11'
1 + [2] // '12' [2].valueOf() returns an object, so `toString` fallback is used, 1 + String([2]), '1' + '2', 12
1 + {} // '1[object Object]' {}.valueOf() is not a primitive, so toString fallback used, String(1) + String({}), '1' + '[object Object]', '1[object Object]'
2 - {} // NaN {}.valueOf() is not a primitive, so toString fallback used => 2 - Number('[object Object]'), NaN
+'a' // NaN `ToPrimitive` passed 'number' hint), Number('a'), NaN
+'' // 0 `ToPrimitive` passed 'number' hint), Number(''), 0
+'-1' // -1 `ToPrimitive` passed 'number' hint), Number('-1'), -1
+{} // NaN `ToPrimitive` passed 'number' hint', `valueOf` returns an object, so falls back to `toString`, Number('[Object object]'), NaN
1 + 'a' // '1a' Both are primitives, one is a string, String(1) + 'a'
1 + {} // '1[object Object]' One primitive, one object, `ToPrimitive` passed no hint, meaning conversion to string will occur, one of the operands is now a string, String(1) + String({}), `1[object Object]`
[] + [] // '' Two objects, `ToPrimitive` passed no hint, String([]) + String([]), '' (empty string)
1 - 'a' // NaN Both are primitives, one is a string, `ToPrimitive` passed 'number' hint, 1-Number('a'), 1-NaN, NaN
1 - {} // NaN One primitive, one is an object, `ToPrimitive` passed 'number' hint, `valueOf` returns object, so falls back to `toString`, 1-Number([object Object]), 1-NaN, NaN
[] - [] // 0 Two objects, `ToPrimitive` passed 'number' hint => `valueOf` returns array instance, so falls back to `toString`, Number('')-Number(''), 0-0, 0
Si noti che l' Dateoggetto intrinseco è unico, in quanto è l'unico intrinseco a sovrascrivere il @@toPrimitivemetodo predefinito , in cui si presume che il suggerimento predefinito sia "stringa" (anziché "numero"). Il motivo per avere questo, è che le Dateistanze traducono in stringhe leggibili per impostazione predefinita, invece del loro valore numerico, per comodità del programmatore. Puoi sovrascrivere i @@toPrimitivetuoi oggetti usando Symbol.toPrimitive.
La seguente griglia mostra i risultati della coercizione per l'operatore di uguaglianza astratto ( ==) ( fonte ):

Vedi anche .
window.console.log (x);oalert (x);?