Pourquoi + est-il si mauvais pour la concaténation?

44

Tout le monde répète que l'un des problèmes de JavaScript est l'utilisation de +[ exemple ] pour la concaténation de chaînes. Certains disent que le problème ne consiste pas à utiliser +, c'est une contrainte de type [voir les commentaires de l'exemple précédent]. Mais les langages fortement typés utilisent + pour concaténer et contraindre les types sans aucun problème. Par exemple, en C #:

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

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

Alors, pourquoi la concaténation de chaînes en JavaScript est-elle un si gros problème?

configurateur
la source
17
pourquoi est-ce bad. Qui est Everyone?
Neal
3
Je pense que le problème est + est un opérateur mathématique. Et que la fonction de concaturation du + confond ce processus standard. parce que tandis que "Hello" + numéro peut fonctionner numéro + "Hello" ne peut pas.
SoylentGray
3
@Neal, je suis d'accord - je veux savoir qui semblent tous ces «tout le monde» mystiques dans toutes ces questions.
GrandmasterB
Utiliser + pour la concaténation est OK. L'utilisation de la saisie dynamique est OK. L'utilisation des deux dans la même langue est une mauvaise chose ^ H ^ H ^ H conduit à l'ambiguïté.
Gerry
6
@Neal - "Tout le monde" serait Douglas Crockford. Il la mentionne comme "horrible" dans son livre "JavaScript: The Good Parts".
kirk.burleson

Réponses:

68

Considérons ce morceau de code JavaScript:

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

Cela va se connecter

le résultat est 1020

Le plus probable n'est pas ce qui était prévu et peut être un bug difficile à détecter.

Mchl
la source
4
Très belle illustration du problème sous-jacent: la concaténation de chaînes n'est pas bien définie lorsque les types (string + int + int dans l'exemple) sont mélangés. Mieux vaut avoir un opérateur totalement différent (comme Perl '.') Avec une sémantique bien définie.
Bruce Ediger
9
Ou la manière de Python de résoudre ce qui serait de vous forcer à envelopper aet bdans str(a)et str(b)appels; sinon, vous obtenez un ValueError: cannot concatenate 'str' and 'int'.
Aphex
6
@FrustratedWithFormsDesigner: ajoutez des parenthèses. 'result is ' + (a + b).
Jon Purdy
18
En C #, vous pouvez faire la même chose et vous obtiendrez le même résultat System.Console.WriteLine("result is " + 10 + 20);, mais personne ne se plaint de cela en C #. C'est ce que je ne comprends pas. Qu'est-ce qui rend le code JavaScript plus déroutant?
configurateur
7
@configurator: parce qu'on dit aux gens que C # est parfait et que javascript est affreux. Ils ont tous les deux tort, mais la réputation d'une langue semble s'en tenir, les perceptions comptent pour beaucoup, en particulier sur les internets.
gbjbaanb
43

Lorsque vous dites "mauvais", vous voulez dire "incorrect" ou voulez-vous dire "lent"? L'argument concernant l'utilisation d'opérateurs mathématiques pour effectuer la concaténation de chaînes est sans doute un argument "incorrect", mais il existe également un argument selon lequel l'utilisation +de nombreuses concaténations de chaînes peut être très lente.

Nous ne parlons pas "Hello" + numberde performance, nous parlons de créer une chaîne relativement grande en l'ajoutant de manière répétée dans une boucle.

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

En JavaScript (et C # d'ailleurs), les chaînes sont immuables. Ils ne peuvent jamais être changés, seulement remplacés par d'autres chaînes. Vous savez probablement que combined + "hello "cela ne modifie pas directement la combinedvariable - l'opération crée une nouvelle chaîne résultant de la concaténation des deux chaînes, mais vous devez ensuite affecter cette nouvelle chaîne à la combinedvariable si vous souhaitez qu'elle soit modifiée.

Cette boucle crée donc un million d'objets chaîne différents et en élimine 999 999. Il n'est pas rapide de créer autant de chaînes qui grossissent continuellement, et le ramasse-miettes a maintenant beaucoup de travail à faire pour nettoyer par la suite.

C # a exactement le même problème, qui est résolu dans cet environnement par la classe StringBuilder . En JavaScript, vous obtiendrez de bien meilleures performances en construisant un tableau de toutes les chaînes que vous souhaitez concaténer, puis en les réunissant une fois à la fin, au lieu d'un million de fois dans la boucle:

var parts = [];
for (var i = 0; i < 1000000; i++) {
    parts.push("hello");
}
var combined = parts.join(" ");
Joel Mueller
la source
3
C'est une excellente réponse. Non seulement a-t-il répondu à la question, mais il a éduqué.
Charles Sprayberry
3
Ce n’est pourtant pas ce que je voulais dire, cela n’a aucun rapport avec la question.
configurateur
4
Désolé. On dirait qu'au moins 7 personnes ont mal compris votre question dans son libellé.
Joel Mueller
9
Re performance: Peut-être que c'était le cas en 2011, mais maintenant la méthode opérateur + est beaucoup plus rapide que la méthode array.join (au moins en v8). Voir ce jsperf: jsperf.com/string-concatenation101
UpTheCreek
2
Avez-vous une idée de la façon dont la méthode de jointure génère une chaîne sur plusieurs pièces en une seule étape, au lieu de combiner ensuite une à une pour générer toutes les chaînes intermédiaires?
Angelo.Hannes
14

Ce n'est pas mal d'utiliser + pour les ajouts et les concaténations, tant que vous ne pouvez ajouter que des nombres et concaténer des chaînes. Cependant, Javascript convertira automatiquement entre les nombres et les chaînes selon vos besoins. Vous aurez donc:

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

Peut-être plus simplement, surcharger "+" en présence de conversion de type automatique annule l'associativité attendue de l'opérateur +:

("x" + 1) + 3 != "x" + (1 + 3)
Kevin Cline
la source
2
La concaténation manque également à la propriété d'addition commutative, 3 + 5 == 5 + 3mais"3" + "5" != "5" + "3"
Caleth
10

Le problème typique évoqué pour la concaténation de chaînes tourne autour de quelques problèmes:

  1. le +symbole étant surchargé
  2. erreurs simples
  3. les programmeurs étant paresseux

#1

Le premier et principal problème de l'utilisation +de la concaténation et de l' ajout de chaînes est l'utilisation que vous utilisez +pour la concaténation et l' ajout de chaînes .

si vous avez des variables aet b, et vous définissez c = a + b, une ambiguïté dépend des types de aet b. Cette ambiguïté vous oblige à prendre des mesures supplémentaires pour atténuer le problème:

Corde Concat:

c = '' + a + b;

Une addition:

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

Cela m'amène à mon deuxième point

# 2

Il est très facile de convertir accidentellement une variable en chaîne sans s'en rendre compte. Il est également facile d'oublier que votre entrée arrive sous forme de chaîne:

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

Produira des résultats non intuitifs car prompt renvoie la valeur de chaîne de l'entrée.

# 3

Il est fastidieux et ennuyeux de devoir taper parseFloatou Number()chaque fois que je veux faire des ajouts. Je me considère assez intelligent pour ne pas gâcher mes types dynamiques, mais cela ne veut pas dire que je n'ai jamais foiré mes types dynamiques . La vérité, c’est que je suis trop paresseux pour taper parseFloatou Number()chaque fois que je fais des ajouts, parce que je fais beaucoup d’ajouts.

Solution?

Toutes les langues ne sont pas utilisées +pour la concaténation de chaînes. PHP utilise .pour concaténer des chaînes, ce qui permet de distinguer entre le moment où vous souhaitez ajouter des nombres et le moment où vous souhaitez joindre des chaînes.

N'importe quel symbole peut être utilisé pour concaténer des chaînes, mais la plupart sont déjà utilisées et il est préférable d'éviter les doublons. Si je le pouvais, j'aurais probablement l'habitude _de joindre des chaînes et de ne pas autoriser les _noms de variables dans.

zzzzBov
la source