TLDR
Typzwang oder implizite Typkonvertierung ermöglicht eine schwache Typisierung und wird in JavaScript verwendet. Die meisten Operatoren (mit Ausnahme der strengen Gleichheitsoperatoren ===und !==) und Wertprüfungsoperationen (z. B. if(value)...) erzwingen die ihnen zugewiesenen Werte, wenn die Typen dieser Werte nicht sofort mit der Operation kompatibel sind.
Der genaue Mechanismus zum Erzwingen eines Werts hängt vom zu bewertenden Ausdruck ab. In der Frage wird der Additionsoperator verwendet.
Der Additionsoperator stellt zunächst sicher, dass beide Operanden Grundelemente sind. In diesem Fall wird die valueOfMethode aufgerufen. Die toStringMethode wird in diesem Fall nicht aufgerufen, da die überschriebene valueOfMethode für das Objekt xeinen primitiven Wert zurückgibt.
Da einer der Operanden in der Frage eine Zeichenfolge ist, werden beide Operanden in Zeichenfolgen konvertiert. Dieser Prozess verwendet die abstrakte interne Operation ToString(Anmerkung: groß geschrieben) und unterscheidet sich von der toStringMethode für das Objekt (oder seine Prototypkette).
Schließlich werden die resultierenden Zeichenfolgen verkettet.
Einzelheiten
Auf dem Prototyp jedes Konstruktorfunktionsobjekts, das jedem Sprachtyp in JavaScript entspricht (dh Number, BigInt, String, Boolean, Symbol und Object), gibt es zwei Methoden: valueOfund toString.
Der Zweck von valueOfbesteht darin, den primitiven Wert abzurufen, der einem Objekt zugeordnet ist (falls vorhanden). Wenn einem Objekt kein primitiver Wert zugrunde liegt, wird das Objekt einfach zurückgegeben.
Wenn valueOfes für ein Grundelement aufgerufen wird, wird das Grundelement auf normale Weise automatisch eingerahmt und der zugrunde liegende Grundelementwert zurückgegeben. Beachten Sie, dass für Zeichenfolgen der zugrunde liegende Grundwert (dh der von zurückgegebene Wert valueOf) die Zeichenfolgendarstellung selbst ist.
Der folgende Code zeigt, dass die valueOfMethode den zugrunde liegenden Grundwert von einem Wrapper-Objekt zurückgibt, und es zeigt, wie unveränderte Objektinstanzen, die nicht Grundelementen entsprechen, keinen Grundwert zurückgeben können, sodass sie sich einfach selbst zurückgeben.
console.log(typeof new Boolean(true)) // 'object'
console.log(typeof new Boolean(true).valueOf()) // 'boolean'
console.log(({}).valueOf()) // {} (no primitive value to return)
Der Zweck von toStringist andererseits die Rückgabe einer Zeichenfolgendarstellung eines Objekts.
Beispielsweise:
console.log({}.toString()) // '[object Object]'
console.log(new Number(1).toString()) // '1'
Bei den meisten Vorgängen versucht JavaScript stillschweigend, einen oder mehrere Operanden in den erforderlichen Typ zu konvertieren. Dieses Verhalten wurde gewählt, um die Verwendung von JavaScript zu vereinfachen. JavaScript hatte anfangs keine Ausnahmen , und dies könnte auch bei dieser Entwurfsentscheidung eine Rolle gespielt haben. Diese Art der impliziten Typkonvertierung wird als Typenzwang bezeichnet und ist die Grundlage für das lose (schwache) JavaScript-Typsystem. Die komplizierten Regeln hinter diesem Verhalten sollen die Komplexität der Typisierung in die Sprache selbst und aus Ihrem Code heraus verschieben.
Während des Zwangsprozesses können zwei Arten der Umwandlung auftreten:
- Konvertierung eines Objekts in ein Grundelement (das möglicherweise eine Typkonvertierung selbst beinhaltet) und
- Direkte Umwandlung auf einen bestimmten Typ Beispiel ein Konstruktor Funktionsobjekt einer der primitiven Typen verwendet (dh.
Number(), Boolean(), String()Etc.)
Umwandlung in ein Primitiv
Beim Versuch, nicht-primitive Typen in zu bearbeitende Primitive zu konvertieren, wird die abstrakte Operation ToPrimitivemit einem optionalen "Hinweis" auf "Zahl" oder "Zeichenfolge" aufgerufen. Wenn der Hinweis weggelassen wird, lautet der Standardhinweis 'number' (es sei denn, die @@toPrimitiveMethode wurde überschrieben). Wenn der Hinweis 'string' ist, toStringwird zuerst versucht, und valueOfzweitens, wenn toStringkein Grundelement zurückgegeben wurde. Sonst umgekehrt. Der Hinweis hängt von der Operation ab, die die Konvertierung anfordert.
Der Additionsoperator liefert keinen Hinweis, wird also valueOfzuerst versucht. Der Subtraktionsoperator gibt einen Hinweis auf 'Zahl' aus, wird also valueOfzuerst versucht. Die einzigen Situationen, die ich in der Spezifikation finden kann, in denen der Hinweis "Zeichenfolge" ist, sind:
Object#toString
- Die abstrakte Operation
ToPropertyKey, die ein Argument in einen Wert konvertiert, der als Eigenschaftsschlüssel verwendet werden kann
Direkte Typkonvertierung
Jeder Bediener hat seine eigenen Regeln für den Abschluss seines Vorgangs. Der Additionsoperator stellt zunächst ToPrimitivesicher, dass jeder Operand ein Grundelement ist. Wenn einer der Operanden eine Zeichenfolge ist, ruft er absichtlich die abstrakte Operation ToStringfür jeden Operanden auf, um das erwartete Verkettungsverhalten der Zeichenfolge mit Zeichenfolgen zu erzielen. Wenn nach dem ToPrimitiveSchritt beide Operanden keine Zeichenfolgen sind, wird eine arithmetische Addition durchgeführt.
Im Gegensatz zur Addition hat der Subtraktionsoperator kein überladenes Verhalten und ruft daher toNumericjeden Operanden auf, der sie zuerst mit in Primitive konvertiert hat ToPrimitive.
So:
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
Beachten Sie, dass das Dateintrinsische Objekt eindeutig ist, da es das einzige intrinsische Objekt ist, @@toPrimitivedas die Standardmethode überschreibt , bei der angenommen wird, dass der Standardhinweis "Zeichenfolge" (anstelle von "Zahl") ist. Der Grund dafür ist, dass DateInstanzen zur Vereinfachung für den Programmierer standardmäßig anstelle ihres numerischen Werts in lesbare Zeichenfolgen übersetzt werden. Sie können @@toPrimitiveIhre eigenen Objekte mit überschreiben Symbol.toPrimitive.
Das folgende Raster zeigt die Zwangsergebnisse für den abstrakten Gleichheitsoperator ( ==) ( Quelle ):

Siehe auch .
window.console.log (x);oderalert (x);?