Entnommen aus MDN
Zeichenfolgenliterale (gekennzeichnet durch doppelte oder einfache Anführungszeichen) und Zeichenfolgen, die von Zeichenfolgenaufrufen in einem Nichtkonstruktorkontext (dh ohne Verwendung des neuen Schlüsselworts) zurückgegeben werden, sind primitive Zeichenfolgen. JavaScript konvertiert Primitive automatisch in String-Objekte, sodass String-Objektmethoden für primitive Strings verwendet werden können. In Kontexten, in denen eine Methode für eine primitive Zeichenfolge aufgerufen werden soll oder eine Eigenschaftssuche erfolgt, wird das Zeichenfolgenprimitiv von JavaScript automatisch umbrochen und die Methode aufgerufen oder die Eigenschaftssuche durchgeführt.
Daher dachte ich, dass (logisch) Operationen (Methodenaufrufe) für Zeichenfolgenprimitive langsamer sein sollten als Operationen für Zeichenfolgenobjekte, da jedes Zeichenfolgenprimitiv in Zeichenfolgenobjekt konvertiert wird (zusätzliche Arbeit), bevor method
es auf die Zeichenfolge angewendet wird.
In diesem Testfall ist das Ergebnis jedoch umgekehrt. Der Codeblock 1 läuft schneller als der Codeblock 2 , beide Codeblöcke sind unten angegeben:
Codeblock-1:
var s = '0123456789';
for (var i = 0; i < s.length; i++) {
s.charAt(i);
}
Codeblock 2:
var s = new String('0123456789');
for (var i = 0; i < s.length; i++) {
s.charAt(i);
}
Die Ergebnisse variieren in Browsern, aber der Codeblock 1 ist immer schneller. Kann jemand bitte erklären, warum der Codeblock-1 schneller ist als der Codeblock-2 .
quelle
new String
führt eine weitere transparente Schicht aus Objektverpackung.typeof new String(); //"object"
'0123456789'.charAt(i)
?code block-1
ist schneller?Antworten:
JavaScript hat zwei Haupttypkategorien, Grundelemente und Objekte.
Die Muster für einfache Anführungszeichen / doppelte Anführungszeichen sind hinsichtlich der Funktionalität identisch. Abgesehen davon wird das Verhalten, das Sie benennen möchten, als Auto-Boxing bezeichnet. Was also tatsächlich passiert, ist, dass ein Grundelement in seinen Wrapper-Typ konvertiert wird, wenn eine Methode des Wrapper-Typs aufgerufen wird. Einfach ausgedrückt:
Ist ein primitiver Datentyp. Es hat keine Methoden, es ist nichts weiter als ein Zeiger auf eine Rohdatenspeicherreferenz, was die viel schnellere Direktzugriffsgeschwindigkeit erklärt.
Was passiert also
s.charAt(i)
zum Beispiel?Da
s
ist keine InstanzString
, Auto-Box JavaScript Willens
, das hattypeof string
seinen Wrapper - Typen,String
mittypeof object
genauers.valueOf(s).prototype.toString.call = [object String]
.Das Auto-Boxing-Verhalten wechselt
s
nach Bedarf hin und her zu seinem Wrapper-Typ, aber die Standardoperationen sind unglaublich schnell, da es sich um einen einfacheren Datentyp handelt. Allerdings Auto-Boxen undObject.prototype.valueOf
haben unterschiedliche Effekte.Wenn Sie das automatische Boxen erzwingen oder ein Grundelement in seinen Wrapper-Typ umwandeln möchten, können Sie es verwenden
Object.prototype.valueOf
, aber das Verhalten ist anders. Basierend auf einer Vielzahl von Testszenarien wendet Auto-Boxing nur die "erforderlichen" Methoden an, ohne die primitive Natur der Variablen zu ändern. Deshalb bekommst du eine bessere Geschwindigkeit.quelle
Dies ist ziemlich implementierungsabhängig, aber ich werde es versuchen. Ich werde mit V8 veranschaulichen, aber ich gehe davon aus, dass andere Motoren ähnliche Ansätze verwenden.
Ein String-Grundelement wird zu einem
v8::String
Objekt analysiert . Daher können Methoden direkt darauf aufgerufen werden, wie von jfriend00 erwähnt .Ein String-Objekt hingegen wird zu einem Objekt analysiert,
v8::StringObject
das sich erstrecktObject
und nicht nur ein vollwertiges Objekt ist, sondern auch als Wrapper für dientv8::String
.Jetzt ist es nur logisch, ein Aufruf von
new String('').method()
muss dies entpackenv8::StringObject
,v8::String
bevor die Methode ausgeführt wird, daher ist es langsamer.In vielen anderen Sprachen haben primitive Werte keine Methoden.
Die Art und Weise, wie MDN es ausdrückt, scheint die einfachste Möglichkeit zu sein, zu erklären, wie das Auto-Boxing von Primitiven funktioniert (wie auch in Flavs Antwort erwähnt), dh wie die primitiven y- Werte von JavaScript Methoden aufrufen können.
Eine Smart Engine konvertiert jedoch nicht jedes Mal, wenn Sie eine Methode aufrufen müssen , ein String- Primitiv-y in ein String-Objekt. Dies wird auch informativ in der Annotated ES5-Spezifikation erwähnt. in Bezug auf die Auflösung von Eigenschaften (und "Methoden" ¹) primitiver Werte:
Auf sehr niedrigem Niveau werden Strings am häufigsten als unveränderliche Skalarwerte implementiert. Beispiel für eine Wrapper-Struktur:
Je weiter Sie vom Primitiven entfernt sind, desto länger wird es dauern, bis Sie es erreichen. In der Praxis sind
String
Grundelemente viel häufiger alsStringObject
s, daher ist es für Engines keine Überraschung, der Klasse der entsprechenden (interpretierten) Objekte der String-Grundelemente Methoden hinzuzufügen, anstatt zwischenString
und hin und her zu konvertieren,StringObject
wie es die Erklärung von MDN nahe legt.¹ In JavaScript ist "Methode" nur eine Namenskonvention für eine Eigenschaft, die in einen Wert vom Typ Funktion aufgelöst wird.
quelle
=]
Jetzt frage ich mich, ob die Erklärung von MDN vorhanden ist, nur weil es der einfachste Weg zu sein scheint, Auto-Boxing zu verstehen, oder ob es in der ES-Spezifikation einen Hinweis darauf gibt Aktualisieren Sie die Antwort, wenn ich jemals eine Referenz finde.Im Falle eines String-Literal können wir keine Eigenschaften zuweisen
Im Fall von String Object können wir Eigenschaften zuweisen
quelle
String
Objekte brauchen . Danke dir!String Literal:
String-Literale sind unveränderlich, was bedeutet, dass ihr Status nach ihrer Erstellung nicht mehr geändert werden kann, wodurch sie auch threadsicher sind.
a==b
Das Ergebnis ist 'true'. Beide Zeichenfolgen verweisen auf dasselbe Objekt.String-Objekt:
Hier werden zwei verschiedene Objekte erstellt, die unterschiedliche Referenzen haben:
a==b
Das Ergebnis ist falsch, da sie unterschiedliche Referenzen haben.quelle
a
&b
versuchen, sie zuzuweisena[0] = 'X'
, wird erfolgreich ausgeführt, funktioniert aber nicht wie erwartetWenn Sie verwenden
new
, geben Sie ausdrücklich an, dass Sie eine Instanz eines Objekts erstellen möchten . Dahernew String
wird ein Objekt erstellt , das das String-Grundelement umschließt, was bedeutet, dass jede Aktion darauf eine zusätzliche Arbeitsebene erfordert.Da es sich um verschiedene Typen handelt, kann Ihr JavaScript- Interpreter sie auch unterschiedlich optimieren, wie in den Kommentaren erwähnt .
quelle
Wenn Sie erklären:
Sie erstellen ein Zeichenfolgenprimitiv. Dieses Zeichenfolgenprimitiv verfügt über Methoden, mit denen Sie Methoden aufrufen können, ohne das Grundelement in ein erstklassiges Objekt zu konvertieren. Ihre Annahme, dass dies langsamer wäre, weil die Zeichenfolge in ein Objekt konvertiert werden muss, ist also nicht korrekt. Es muss nicht in ein Objekt konvertiert werden. Das Grundelement selbst kann die Methoden aufrufen.
Das Konvertieren in ein vollständiges Objekt (mit dem Sie neue Eigenschaften hinzufügen können) ist ein zusätzlicher Schritt und beschleunigt die Zeichenfolgenoperationen nicht schneller (tatsächlich zeigt Ihr Test, dass sie dadurch langsamer werden).
quelle
String.prototype
?var s = '0123456789';
ist ein primitiver Wert, wie kann dieser Wert Methoden haben, ich bin verwirrt!Ich kann sehen, dass diese Frage vor langer Zeit gelöst wurde. Es gibt eine weitere subtile Unterscheidung zwischen String-Literalen und String-Objekten, da niemand sie berührt zu haben scheint. Ich dachte, ich würde sie der Vollständigkeit halber nur schreiben.
Grundsätzlich besteht eine weitere Unterscheidung zwischen beiden bei der Verwendung von eval. eval ('1 + 1') ergibt 2, während eval (neuer String ('1 + 1')) '1 + 1' ergibt. Wenn also ein bestimmter Codeblock sowohl 'normal' als auch mit eval ausgeführt werden kann, könnte dies der Fall sein führen zu seltsamen Ergebnissen
quelle
new String("")
Geben Sie ein Objekt zurück und bewerten Sie nur die Zeichenfolge und geben Sie alles andere so zurück, wie es istDie Existenz eines Objekts hat wenig mit dem tatsächlichen Verhalten eines Strings in ECMAScript / JavaScript-Engines zu tun, da der Stammbereich lediglich Funktionsobjekte dafür enthält. Daher wird die Funktion charAt (int) im Fall eines Zeichenfolgenliteral gesucht und ausgeführt.
Bei einem realen Objekt fügen Sie eine weitere Ebene hinzu, in der die charAt (int) -Methode auch für das Objekt selbst durchsucht wird, bevor das Standardverhalten einsetzt (wie oben). Anscheinend wird in diesem Fall überraschend viel Arbeit geleistet.
Übrigens glaube ich nicht, dass Grundelemente tatsächlich in Objekte konvertiert werden, aber die Skript-Engine markiert diese Variable einfach als Zeichenfolgentyp und kann daher alle bereitgestellten Funktionen dafür finden, sodass es so aussieht, als würden Sie ein Objekt aufrufen. Vergessen Sie nicht, dass dies eine Skriptlaufzeit ist, die nach anderen Prinzipien als eine OO-Laufzeit arbeitet.
quelle
Der größte Unterschied zwischen einem Zeichenfolgenprimitiv und einem Zeichenfolgenobjekt besteht darin, dass Objekte für den
==
Operator dieser Regel folgen müssen :Während String-Grundelemente einen praktischen
==
Wert haben, der den Wert vergleicht, haben Sie kein Glück, wenn es darum geht, dass sich ein anderer unveränderlicher Objekttyp (einschließlich eines String-Objekts) wie ein Werttyp verhält.(Andere haben festgestellt, dass ein Zeichenfolgenobjekt technisch veränderbar ist, da Sie ihm Eigenschaften hinzufügen können. Es ist jedoch nicht klar, wofür dies nützlich ist. Der Zeichenfolgenwert selbst ist nicht veränderbar.)
quelle
Der Code wird optimiert, bevor er von der Javascript-Engine ausgeführt wird. Im Allgemeinen können Mikro-Benchmarks irreführend sein, da Compiler und Interpreter Teile Ihres Codes neu anordnen, ändern, entfernen und andere Tricks ausführen, damit er schneller ausgeführt wird. Mit anderen Worten, der geschriebene Code gibt an, was das Ziel ist, aber der Compiler und / oder die Laufzeit entscheiden, wie dieses Ziel erreicht werden soll.
Block 1 ist schneller, hauptsächlich wegen: var s = '0123456789'; ist immer schneller als var s = new String ('0123456789'); wegen des Overheads der Objekterstellung.
Der Schleifenteil ist nicht derjenige, der die Verlangsamung verursacht, da chartAt () vom Interpreter eingefügt werden kann. Versuchen Sie, die Schleife zu entfernen, und führen Sie den Test erneut aus. Sie werden feststellen, dass das Geschwindigkeitsverhältnis dem entspricht, als ob die Schleife nicht entfernt worden wäre. Mit anderen Worten, für diese Tests haben die Schleifenblöcke zur Ausführungszeit genau den gleichen Bytecode / Maschinencode.
Bei diesen Arten von Mikro-Benchmarks liefert ein Blick auf den Bytecode oder den Maschinencode ein klareres Bild.
quelle
In Javascript sind primitive Datentypen wie string ein nicht zusammengesetzter Baustein. Dies bedeutet, dass es sich nur um Werte handelt, nicht mehr:
let a = "string value";
Standardmäßig gibt es keine integrierten Methoden wie toUpperCase, toLowerCase usw.Aber wenn Sie versuchen zu schreiben:
Dies wird keinen Fehler auslösen, stattdessen funktionieren sie wie sie sollten.
Was ist passiert ? Wenn Sie versuchen, auf eine Eigenschaft einer Zeichenfolge
a
zuzugreifen, zwingt Javascript die Zeichenfolge zu einem Objekt,new String(a);
das als Wrapper-Objekt bezeichnet wird .Dieser Prozess ist mit dem Konzept der Funktionskonstruktoren in Javascript verknüpft , bei dem Funktionen zum Erstellen neuer Objekte verwendet werden.
Wenn Sie geben
new String('String value');
hier String Funktion Konstruktor, der ein Argument nimmt und erzeugt ein leeres Objekt innerhalb des Funktionsumfang, das leere Objekt zugewiesen ist dies , und in diesem Fall liefert String alle diejenigen , die in Funktionen gebaut bekannt wir bereits erwähnt. Sobald der Vorgang abgeschlossen ist, z. B. in Großbuchstaben, wird das Wrapper-Objekt verworfen.Um dies zu beweisen, machen wir Folgendes:
Hier wird die Ausgabe undefiniert. Warum ? In diesem Fall erstellt Javascript ein Wrapper-String-Objekt, legt die neue Eigenschaft addNewProperty fest und verwirft das Wrapper-Objekt sofort. Deshalb wirst du undefiniert. Pseudocode würde folgendermaßen aussehen:
quelle
Wir können String auf drei Arten definieren
// wir können auch mit 4 erstellen. var d = a + '';
Überprüfen Sie den Typ der mit dem Operator typeof erstellten Zeichenfolgen
wenn Sie a und b var vergleichen
a==b ( // yes)
wenn Sie ein String-Objekt vergleichen
quelle