¿Por qué + es tan malo para la concatenación?

44

Todo el mundo sigue diciendo que uno de los problemas de JavaScript es usar +[ ejemplo ] para la concatenación de cadenas. Algunos dicen que el problema no está usando +, es coerción de tipo [ver los comentarios del ejemplo anterior]. Pero los lenguajes fuertemente tipados usan + para los tipos de concatenación y coerción sin ningún problema. Por ejemplo, en C #:

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

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

Entonces, ¿por qué la concatenación de cadenas en JavaScript es un problema tan grande?

configurador
fuente
17
¿por qué es bad. ¿Quién es Everyone?
Neal
3
Creo que el problema es + es un operador matemático. Y que la función de concatenación de + confunde ese proceso estándar. porque mientras "Hola" + número puede funcionar, el número + "Hola" puede no funcionar
SoylentGray
3
@Neal, estoy de acuerdo: quiero saber quiénes son todos estos místicos 'todos' en todas estas preguntas.
GrandmasterB
Usar + para concatenación está bien. Usar tipeo dinámico está bien. Usar ambos en el mismo idioma es malo ^ H ^ H ^ H conduce a la ambigüedad.
Gerry
66
@Neal - "Todos" serían Douglas Crockford. Lo enumera como "horrible" en su libro, 'JavaScript: The Good Parts'.
kirk.burleson

Respuestas:

68

Considere esta pieza de código JavaScript:

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

Esto registrará

el resultado es 1020

Lo más probable es que no sea lo que se pretendía, y puede ser un error difícil de rastrear.

Mchl
fuente
44
Muy buena ilustración del problema subyacente: la concatenación de cadenas no está bien definida cuando los tipos (cadena + int + int en el ejemplo) se mezclan. Es mejor tener un operador completamente diferente (como el '.' De Perl) con una semántica bien definida.
Bruce Ediger
99
O forma de resolver este Python, lo que sería para forzarlo para envolver ay ben str(a)y str(b)llamadas; de lo contrario, obtienes un ValueError: cannot concatenate 'str' and 'int'.
Aphex
66
@FrustratedWithFormsDesigner: Agregar paréntesis. 'result is ' + (a + b).
Jon Purdy
18
La cosa es que en C # podrías hacer lo mismo y obtendrás el mismo resultado System.Console.WriteLine("result is " + 10 + 20);, pero nadie se queja de eso en C #. Eso es lo que no entiendo: ¿qué tiene JavaScript que lo hace más confuso?
configurador
77
@configurator: porque a las personas se les dice que C # es perfecto y javascript es horrible. Ambos están equivocados, pero la reputación de un idioma parece quedarse, las percepciones cuentan mucho, especialmente en Internet.
gbjbaanb
43

Cuando dice "malo", ¿quiere decir "incorrecto" o "lento"? El argumento sobre el uso de los operadores matemáticos para hacer la concatenación de cadenas es sin duda un argumento "incorrecto", pero también hay un argumento para afirmar que el uso de +hacer una gran cantidad de concatenación de cadenas puede ser muy lento.

No estamos hablando de "Hello" + numbercuando hablamos de rendimiento, estamos hablando de construir una cadena relativamente grande al agregarla repetidamente en un bucle.

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

En JavaScript (y C # para el caso) las cadenas son inmutables. Nunca se pueden cambiar, solo se pueden reemplazar con otras cadenas. Probablemente sepa que combined + "hello "no modifica directamente la combinedvariable: la operación crea una nueva cadena que es el resultado de concatenar las dos cadenas juntas, pero luego debe asignar esa nueva cadena a la combinedvariable si desea que se cambie.

Entonces, lo que está haciendo este bucle es crear un millón de objetos de cadena diferentes y tirar 999,999 de ellos. Crear tantas cadenas que crecen continuamente en tamaño no es rápido, y ahora el recolector de basura tiene mucho trabajo que hacer para limpiar después de esto.

C # tiene exactamente el mismo problema, que la clase StringBuilder resuelve en ese entorno . En JavaScript, obtendrá un rendimiento mucho mejor al construir una matriz de todas las cadenas que desea concatenar, y luego unirlas una vez al final, en lugar de un millón de veces en el bucle:

var parts = [];
for (var i = 0; i < 1000000; i++) {
    parts.push("hello");
}
var combined = parts.join(" ");
Joel Mueller
fuente
3
Esta es una excelente respuesta. No solo respondió la pregunta, sino que educó.
Charles Sprayberry
3
Sin embargo, esto no es lo que quise decir, no tiene nada que ver con la pregunta.
configurador
44
Lo siento. Parece que al menos 7 personas entendieron mal su pregunta por la forma en que fue redactada.
Joel Mueller
99
Re rendimiento: tal vez este fue el caso en 2011, pero ahora el método del operador + es mucho más rápido que el método array.join (en v8 al menos). Vea este jsperf: jsperf.com/string-concatenation101
UpTheCreek
2
¿Alguna idea de cómo el método de unión genera una cadena de varias partes en un solo paso, en lugar de combinar también una por una, generando así todas esas cadenas intermedias?
Angelo.Hannes
14

No está mal usar + tanto para la suma como para la concatenación, siempre y cuando solo pueda agregar números y concatenar cadenas. Sin embargo, Javascript convertirá automáticamente entre números y cadenas según sea necesario, por lo que tiene:

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

Quizás más simple, sobrecargar "+" en presencia de conversión automática de tipo rompe la asociatividad esperada del operador +:

("x" + 1) + 3 != "x" + (1 + 3)
Kevin Cline
fuente
2
También la propiedad conmutativa de la suma falta en la concatenación 3 + 5 == 5 + 3pero"3" + "5" != "5" + "3"
Caleth
10

El problema típico que surge para la concatenación de cadenas gira en torno a algunos problemas:

  1. el +símbolo se sobrecarga
  2. errores simples
  3. programadores siendo perezosos

# 1

El primer y principal problema con el uso +para la concatenación y adición de cadenas es que está usando +para la concatenación y adición de cadenas .

si tiene variables ay b, y establece c = a + b, existe una ambigüedad que depende de los tipos de ay b. Esta ambigüedad lo obliga a tomar medidas adicionales para mitigar el problema:

Concat de cuerda:

c = '' + a + b;

Adición:

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

Esto me lleva al segundo punto

# 2

Es muy fácil convertir accidentalmente una variable en una cadena sin darse cuenta. También es fácil olvidar que su entrada viene como una cadena:

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

Producirá resultados poco intuitivos porque el indicador devuelve el valor de cadena de la entrada.

# 3

Necesitar escribir parseFloato Number()cada vez que quiero hacer una suma es tedioso y molesto. Me considero lo suficientemente inteligente como para recordar no estropear mis tipos dinámicos, pero eso no significa que nunca haya estropeado mis tipos dinámicos . La verdad del asunto es que soy demasiado vago para escribir parseFloato Number()cada vez que hago una suma, porque hago mucha suma.

¿Solución?

No todos los idiomas se usan +para la concatenación de cadenas. PHP utiliza .para concatenar cadenas, lo que ayuda a distinguir entre cuándo desea agregar números y cuándo desea unir cadenas.

Cualquier símbolo podría usarse para concatenar cadenas, pero la mayoría ya se usa, y es mejor evitar la duplicación. Si me saliera con la mía, probablemente usaría _para unir cadenas y no permitir los _nombres de variables.

zzzzBov
fuente