48

Everybody keeps saying that one of JavaScript's problems is using + [example] for string concatenation. Some say the problem is not using +, it's type coercion [see the comments from the previous example]. But strongly-typed languages use + for concatenation and coerce types without any problem. For example, in C#:

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

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

So why is it that string concatenation in JavaScript is such a big problem?

configurator
  • 2,796
  • 2
  • 21
  • 23
  • 19
    why is it `bad`. Who is `Everyone`? – Naftali Jul 06 '11 at 19:23
  • 3
    I think the problem is + is a mathmatical operator. And that the concatination function of the + confuses that standard process. because while "Hello" + number may work number + "Hello" may not. – SoylentGray Jul 06 '11 at 19:36
  • 3
    @Neal, I agree - I want to know who all these mystical 'everyones' seem to be in all these questions. – GrandmasterB Jul 06 '11 at 20:34
  • Using + for concatenation is OK. Using dynamic typing is OK. Using both in the same language is bad^H^H^H leads to ambiguity. – Gerry Jul 06 '11 at 22:46
  • 7
    @Neal - "Everyone" would be Douglas Crockford. He lists it as "awful" in his book, 'JavaScript: The Good Parts'. – kirk.burleson Jul 06 '11 at 23:03
  • You can just use [`str.concat(a, b, c);`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/concat) if you want. – jsejcksn May 20 '16 at 05:00

4 Answers4

73

Consider this piece of JavaScript code:

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

This will log

result is 1020

Which most likely is not what was intended, and can be a hard to track bug.

Mchl
  • 4,103
  • 1
  • 22
  • 23
  • 4
    Very nice illustration of the underlying problem: string concatenation isn't well-defined when types (string + int + int in the example) get mixed. Better to have an entirely different operator (like Perl's '.') with well-defined semantics. – Bruce Ediger Jul 06 '11 at 20:02
  • Worling most of my time with PHP I wish it used something else than Perl's . for concatenation. It could be used as namespace separator in place of ugly \ – Mchl Jul 06 '11 at 20:06
  • 9
    Or Python's way of solving this, which would be to force you to wrap `a` and `b` in `str(a)` and `str(b)` calls; otherwise, you get a `ValueError: cannot concatenate 'str' and 'int'`. – Aphex Jul 06 '11 at 20:11
  • It's been a long time since I've worked with JavaScript. How would you fix this so the output is `result is 30`? – FrustratedWithFormsDesigner Jul 06 '11 at 20:37
  • 7
    @FrustratedWithFormsDesigner: Add parentheses. `'result is ' + (a + b)`. – Jon Purdy Jul 06 '11 at 20:46
  • 19
    Thing is, in C# you could do the same thing, and you will get the same result - `System.Console.WriteLine("result is " + 10 + 20);`, but nobody ever complains about that in C#. That's what I don't understand - what is it about JavaScript that makes it more confusing? – configurator Jul 06 '11 at 23:14
  • @configurator: Beats me. I did never work with C#. Perhaps C# uses type checking to avoid similar errors (of not this specific one). – Mchl Jul 07 '11 at 10:19
  • 7
    @configurator: because people are told C# is perfect and javascript is awful. They're both wrong, but a language's reputation seems to stick, perceptions count for a lot, especially on the internets. – gbjbaanb Aug 28 '12 at 12:32
  • 1
    because in JavaScript you can assign a string to one of the variables instead of an int and it changes the function of the operator. – Ewan Oct 08 '15 at 18:05
  • @configurator: In Javascript, "2"-1 and "2"+1 are both legal, and have totally different interpretations. Allowing the latter is not good, but C# code which confuses strings and numbers will usually get caught by the compiler somewhere. HyperTalk on the Macintosh would IIRC interpret "2-1" as 1, but "2"+1 or "2"+"1" as 3; since concatenation required the "&" operator, even if both operands were strings, comparisons were a problem (goofy and non-transitive) but using different operators for concatenation *or* disallowing strings from working as numbers can avoid trouble; JS does neither. – supercat Nov 11 '15 at 17:47
  • That may not be what the developer intended, but that is a well defined operation that is easily understood. It's doing a string concatenation, so it's not going to do math for you. If I typed that code, I would get the exact result I expected. So how is that Javascript's fault? Or the '+' operator's fault? – iamtheddrman Jan 06 '17 at 15:20
  • It's not anyone's fault. It can just be confusing. Heck, I've been working with Javascript for almost 10 years now, and still occasionally I fall for this. – Mchl Jan 06 '17 at 15:25
  • @configurator because that's just one example among many of Javascript's confusing use of the + operator. It's bad when C# does it and it's bad when Javascript does it, but Javascript has many more examples. – DavidS Jun 02 '17 at 17:35
49

When you say "bad" do you mean "incorrect" or do you mean "slow"? The argument about using mathematical operators to do string concatenation is arguably an "incorrect" argument, but there's also an argument to be made that using + to do a lot of string concatenation can be very slow.

We're not talking about "Hello" + number when we talk about performance, we're talking about building up a relatively large string by repeatedly appending to it in a loop.

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

In JavaScript (and C# for that matter) strings are immutable. They can never be changed, only replaced with other strings. You're probably aware that combined + "hello " doesn't directly modify the combined variable - the operation creates a new string that is the result of concatenating the two strings together, but you must then assign that new string to the combined variable if you want it to be changed.

So what this loop is doing is creating a million different string objects, and throwing away 999,999 of them. Creating that many strings that are continually growing in size is not fast, and now the garbage collector has a lot of work to do to clean up after this.

C# has the exact same problem, which is solved in that environment by the StringBuilder class. In JavaScript, you'll get much better performance by building up an array of all the strings you want to concatenate, and then joining them together one time at the end, instead of a million times in the loop:

var parts = [];
for (var i = 0; i < 1000000; i++) {
    parts.push("hello");
}
var combined = parts.join(" ");
Joel Mueller
  • 590
  • 3
  • 6
  • 4
    This is an excellent answer. Not only did it answer the question but it educated. – Charles Sprayberry Jul 07 '11 at 00:02
  • 4
    This isn't what I meant at all though, it's completely unrelated to the question. – configurator Jul 07 '11 at 18:37
  • 4
    Sorry. Looks like at least 7 people misunderstood your question from the way it was worded. – Joel Mueller Jul 07 '11 at 20:28
  • Exactly what I was looking for... – Karthik Nov 28 '13 at 18:32
  • 10
    Re performance : Maybe this was the case in 2011, but now the + operator method is much faster than than the array.join method (in v8 at least). See this jsperf : http://jsperf.com/string-concatenation101 – UpTheCreek Jun 18 '14 at 13:10
  • @UpTheCreek Thanks for the information, that's really good to know. I think it's interesting how much faster Firefox is than Chrome at that particular benchmark, too. Unfortunately, I still have to support IE 8, so it might as well still be 2011 for me. I tried to run that particular benchmark with IE 8, but the constant "stop running this script?" popups make the results suspect at best. IE 9 basically has the two approaches tied at "incredibly pathetic." – Joel Mueller Jun 19 '14 at 03:50
  • 2
    Any idea how the join method generates one string out of multiple parts in one step, instead of also combining then one by one, thus also generating all those intermediate strings? – Angelo.Hannes Nov 12 '14 at 15:02
  • It can be implemented in C++, dynamically growing the space allocated for the string [by a factor of 1.5x or 2x](https://stackoverflow.com/questions/1100311/what-is-the-ideal-growth-rate-for-a-dynamically-allocated-array), leading to linear amortized cost. The naive Javascript implementation is quadratic. – Brandon Jan 27 '15 at 18:15
  • Mildly interesting paper on this topic: http://arxiv.org/pdf/1503.02678v1.pdf – msanford Mar 26 '15 at 20:50
  • 1
    @Angelo.Hannes - I don't know exactly how it's implemented in practice, but one way to do it would be to add up the lengths of all the strings in the array, plus the length of the delimiter times the number of items in the array minus one. Then allocate a string with that many characters in it, and fill in the bits. – Joel Mueller Mar 27 '15 at 21:57
  • In js you can do: `'
    \ [insert line break]
    ';`
    – SuperUberDuper Feb 03 '16 at 02:56
  • 1
    Historically interesting answer but probably no longer relevant on modern JavaScript engines: *"modern JavaScript engines optimize the + operator internally [...]. Hence there is no need for StringBuilder in JavaScript. Just use += and be done"* http://2ality.com/2011/10/string-concatenation.html – le_m Nov 23 '17 at 03:22
15

It's not bad to use + for both addition and concatenation, as long as you can only add numbers, and only concatenate strings. However, Javascript will automatically convert between numbers and strings as needed, so you have:

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

Perhaps more simply, overloading "+" in the presence of automatic type conversion breaks the expected associativity of the + operator:

("x" + 1) + 3 != "x" + (1 + 3)
kevin cline
  • 33,608
  • 3
  • 71
  • 142
  • 3
    Also the commutative property of addition is missing from concatenation `3 + 5 == 5 + 3` but `"3" + "5" != "5" + "3"` – Caleth Oct 08 '15 at 15:33
10

The typical issue brought up for string concatenation revolves around a few issues:

  1. the + symbol being overloaded
  2. simple mistakes
  3. programmers being lazy

#1

The first and foremost issue with using + for string concatenation and addition is that you're using + for string concatenation and addition.

if you have variables a and b, and you set c = a + b, there is an ambiguity dependent on the types of a and b. This ambiguity forces you to take extra steps to mitigate the issue:

String Concat:

c = '' + a + b;

Addition:

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

This brings me to my second point

#2

It's very easy to accidentally cast a variable to a string without realizing it. It's also easy to forget that your input is coming in as a string:

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

Will produce unintuitive results because prompt returns the string value of the input.

#3

Needing to type parseFloat or Number() every time I want to do addition is tedious and annoying. I think of myself as smart enough to remember not to mess up my dynamic types, but that doesn't mean I've never messed up my dynamic types. The truth of the matter is that I'm too lazy to type out parseFloat or Number() every time I do addition, because I do a lot of addition.

Solution?

Not all languages use + for string concatenation. PHP uses . to concatenate strings, which helps distinguish between when you want to add numbers and when you want to join strings.

Any symbol could be used to concatenate strings, but most are already used, and it's best to avoid duplication. If I had my way, I'd probably use _ to join strings, and disallow the _ in variable names.

zzzzBov
  • 5,794
  • 1
  • 27
  • 28