Warum ist + so schlecht für die Verkettung?

44

Jeder sagt immer wieder, dass eines der Probleme von JavaScript darin besteht, +[ example ] für die Verkettung von Zeichenfolgen zu verwenden. Einige sagen, das Problem wird nicht verwendet +, es ist Typenzwang [siehe die Kommentare aus dem vorherigen Beispiel]. Stark typisierte Sprachen verwenden jedoch +, um Typen problemlos zu verketten und zu erzwingen. Zum Beispiel in C #:

int number = 1;
MyObject obj = new MyObject();

var result = "Hello" + number + obj;
// is equivalent to
string result = "hello" + number.ToString() + obj.ToString();

Warum ist die Verkettung von Zeichenfolgen in JavaScript ein so großes Problem?

Konfigurator
quelle
17
warum ist es bad. Wer ist Everyone?
Neal
3
Ich denke, das Problem ist, dass + ein mathematischer Operator ist. Und dass die Concatination-Funktion des + diesen Standardprozess verwirrt. denn während "hallo" + nummer kann funktionieren nummer + "hallo" kann nicht.
SoylentGray
3
@Neal, ich stimme zu - ich möchte wissen, wer all diese mystischen "Jedermann" in all diesen Fragen zu sein scheinen.
GroßmeisterB
Die Verwendung von + für die Verkettung ist in Ordnung. Die Verwendung der dynamischen Eingabe ist in Ordnung. Es ist schlecht, beide in derselben Sprache zu verwenden. ^ H ^ H ^ H führt zu Mehrdeutigkeiten.
Gerry
6
@ Neal - "Jeder" wäre Douglas Crockford. Er listet es in seinem Buch "JavaScript: The Good Parts" als "schrecklich" auf.
kirk.burleson

Antworten:

68

Betrachten Sie diesen JavaScript-Code:

var a = 10;
var b = 20;
console.log('result is ' + a + b);

Dies wird protokolliert

Ergebnis ist 1020

Was höchstwahrscheinlich nicht das ist, was beabsichtigt war, und ein schwer zu verfolgender Fehler sein kann.

Mchl
quelle
4
Sehr schöne Illustration des zugrunde liegenden Problems: Die Verkettung von Zeichenfolgen ist nicht genau definiert, wenn Typen (Zeichenfolge + int + int im Beispiel) gemischt werden. Es ist besser, einen völlig anderen Operator (wie Perls '.') Mit einer genau definierten Semantik zu haben.
Bruce Ediger
9
Oder Pythons Weg , dies zu lösen, die Sie wäre zu zwingen , zu wickeln aund bin str(a)und str(b)Anrufe; ansonsten bekommst du eine ValueError: cannot concatenate 'str' and 'int'.
Aphex
6
@FrustratedWithFormsDesigner: Klammern hinzufügen. 'result is ' + (a + b).
Jon Purdy
18
In C # könnten Sie dasselbe tun, und Sie erhalten dasselbe Ergebnis - System.Console.WriteLine("result is " + 10 + 20);aber niemand beschwert sich jemals darüber in C #. Das verstehe ich nicht - was macht JavaScript so verwirrend?
Konfigurator
7
@configurator: weil den Leuten gesagt wird, dass C # perfekt und Javascript schrecklich ist. Sie sind beide falsch, aber der Ruf einer Sprache scheint zu bleiben, die Wahrnehmung spielt eine große Rolle, besonders im Internet.
gbjbaanb
43

Wenn Sie "schlecht" sagen, meinen Sie dann "falsch" oder "langsam"? Das Argument über die Verwendung von mathematischen Operatoren String - Verkettung zu tun , ist wohl ein „falsche“ Argument, aber es gibt auch ein Argument gemacht werden , dass die Verwendung +zu tun eine Menge von String - Verkettung kann sehr langsam sein.

Wir "Hello" + numberreden nicht über Performance, wir reden über den Aufbau einer relativ großen Zeichenkette, indem wir sie wiederholt in einer Schleife anhängen.

var combined = "";
for (var i = 0; i < 1000000; i++) {
    combined = combined + "hello ";
}

In JavaScript (und C #) sind Zeichenfolgen unveränderlich. Sie können niemals geändert, sondern nur durch andere Saiten ersetzt werden. Sie wissen wahrscheinlich, dass combined + "hello "die combinedVariable nicht direkt geändert wird - die Operation erstellt eine neue Zeichenfolge, die das Ergebnis der Verknüpfung der beiden Zeichenfolgen ist. Sie müssen diese neue Zeichenfolge jedoch der combinedVariablen zuweisen, wenn sie geändert werden soll.

Diese Schleife erstellt also eine Million verschiedener Zeichenfolgenobjekte und wirft 999.999 davon weg. Es ist nicht schnell, so viele Zeichenfolgen zu erstellen, die ständig größer werden, und jetzt muss der Garbage Collector eine Menge Arbeit leisten, um danach aufzuräumen.

C # hat genau dasselbe Problem, das in dieser Umgebung von der StringBuilder- Klasse gelöst wird . In JavaScript erzielen Sie eine viel bessere Leistung, wenn Sie ein Array aller zu verkettenden Zeichenfolgen erstellen und sie am Ende einmal anstatt millionenfach in der Schleife zusammenfügen:

var parts = [];
for (var i = 0; i < 1000000; i++) {
    parts.push("hello");
}
var combined = parts.join(" ");
Joel Mueller
quelle
3
Dies ist eine hervorragende Antwort. Es beantwortete nicht nur die Frage, sondern erzog auch.
Charles Sprayberry
3
Das habe ich aber überhaupt nicht gemeint, es hat überhaupt nichts mit der Frage zu tun.
Konfigurator
4
Es tut uns leid. Sieht so aus, als hätten mindestens 7 Personen Ihre Frage falsch verstanden.
Joel Mueller
9
Zu Leistung: Vielleicht war dies 2011 der Fall, aber jetzt ist die Operatormethode + viel schneller als die Methode array.join (zumindest in Version 8). Siehe hierzu jsperf: jsperf.com/string-concatenation101
UpTheCreek
2
Haben Sie eine Idee, wie die Join-Methode einen String aus mehreren Teilen in einem Schritt generiert, anstatt dann auch einen nach dem anderen zu kombinieren und so auch alle diese Zwischen-Strings zu generieren?
Angelo.Hannes
14

Es ist nicht schlecht, + sowohl für das Hinzufügen als auch für das Verketten zu verwenden, solange Sie nur Zahlen hinzufügen und nur Zeichenfolgen verketten können. JavaScript konvertiert jedoch bei Bedarf automatisch zwischen Zahlen und Zeichenfolgen. Sie haben also folgende Möglichkeiten:

3 * 5 => 15
"3" * "5" => 15
2 + " fish" => "2 fish"
"3" + "5" => "35"  // hmmm...

Das Überladen von "+" bei Vorhandensein einer automatischen Typkonvertierung bricht möglicherweise einfacher die erwartete Assoziativität des Operators +:

("x" + 1) + 3 != "x" + (1 + 3)
Kevin Cline
quelle
2
Auch die kommutative Eigenschaft der Addition fehlt in der Verkettung, 3 + 5 == 5 + 3aber"3" + "5" != "5" + "3"
Caleth
10

Das typische Problem, das bei der Verkettung von Zeichenfolgen auftritt, betrifft einige Probleme:

  1. Das +Symbol ist überladen
  2. einfache fehler
  3. Programmierer sind faul

# 1

Das erste und wichtigste Problem bei der Verwendung +für die Verkettung und Hinzufügung von Zeichenfolgen ist, dass Sie +für die Verkettung und Hinzufügung von Zeichenfolgen verwenden .

Wenn Sie Variablen aund bhaben und festlegen c = a + b, gibt es eine Mehrdeutigkeit, die von den Typen aund abhängt b. Diese Mehrdeutigkeit zwingt Sie dazu, zusätzliche Schritte zu unternehmen, um das Problem zu beheben:

String Concat:

c = '' + a + b;

Zusatz:

c = parseFloat(a) + parseFloat(b);

Dies bringt mich zu meinem zweiten Punkt

# 2

Es ist sehr einfach, eine Variable versehentlich in einen String umzuwandeln, ohne es zu merken. Es ist auch leicht zu vergessen, dass Ihre Eingabe als Zeichenfolge eingeht:

a = prompt('Pick a number');
b = prompt('Pick a second number');
alert( a + b );

Erzeugt nicht intuitive Ergebnisse, da die Eingabeaufforderung den Zeichenfolgenwert der Eingabe zurückgibt.

#3

Zu tippen parseFloatoder Number()jedes Mal, wenn ich etwas hinzufügen möchte, ist mühsam und nervig. Ich halte mich für klug genug, um daran zu denken, meine dynamischen Typen nicht durcheinander zu bringen , aber das bedeutet nicht, dass ich meine dynamischen Typen nie durcheinander gebracht habe . Die Wahrheit ist, dass ich zu faul bin, um zu tippen, parseFloatoder Number()jedes Mal, wenn ich Addition mache, weil ich viel Addition mache .

Lösung?

Nicht alle Sprachen werden +für die Verkettung von Zeichenfolgen verwendet. PHP verwendet, .um Zeichenfolgen zu verketten, wodurch unterschieden werden kann, wann Sie Zahlen hinzufügen und wann Sie Zeichenfolgen verbinden möchten.

Jedes Symbol kann zum Verketten von Zeichenfolgen verwendet werden, die meisten werden jedoch bereits verwendet. Vermeiden Sie daher Doppelungen. Wenn es nach mir ginge, würde ich wahrscheinlich verwenden _, um Zeichenfolgen zu verbinden und die _in-Variablennamen nicht zuzulassen.

zzzzBov
quelle