Seems based on benchmarks at JSPerf that using += is the fastest method, though not necessarily in every browser.
For building strings in the DOM, it seems to be better to concatenate the string first and then add to the DOM, rather then iteratively add it to the dom. You should benchmark your own case though.
(Thanks @zAlbee for correction)
Answer from Jakub Hampl on Stack OverflowSeems based on benchmarks at JSPerf that using += is the fastest method, though not necessarily in every browser.
For building strings in the DOM, it seems to be better to concatenate the string first and then add to the DOM, rather then iteratively add it to the dom. You should benchmark your own case though.
(Thanks @zAlbee for correction)
I have no comment on the concatenation itself, but I'd like to point out that @Jakub Hampl's suggestion:
For building strings in the DOM, in some cases it might be better to iteratively add to the DOM, rather then add a huge string at once.
is wrong, because it's based on a flawed test. That test never actually appends into the DOM.
This fixed test shows that creating the string all at once before rendering it is much, MUCH faster. It's not even a contest.
(Sorry this is a separate answer, but I don't have enough rep to comment on answers yet.)
I have a server that uses the chat gpt api with stream (SSE) and I have to concatenate all the chucks it sends.
I wanted to know if there is something more efficient than +=, in other languages they recommend using a String Buffer, in js/ts I don't know what is best.
Thank you so much.
async function main() {
const stream = await openai.chat.completions.create({
model: 'gpt-4',
messages: [{ role: 'user', content: 'Say this is a test' }],
stream: true,
});
for await (const part of stream) {
process.stdout.write(part.choices[0]?.delta?.content || '');
}
}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.
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(" ");
Browser string optimizations have changed the string concatenation picture.
Firefox was the first browser to optimize string concatenation. Beginning with version 1.0, the array technique is actually slower than using the plus operator in all cases. Other browsers have also optimized string concatenation, so Safari, Opera, Chrome, and Internet Explorer 8 also show better performance using the plus operator. Internet Explorer prior to version 8 didn’t have such an optimization, and so the array technique is always faster than the plus operator.
— Writing Efficient JavaScript: Chapter 7 – Even Faster Websites
The V8 javascript engine (used in Google Chrome) uses this code to do string concatenation:
// ECMA-262, section 15.5.4.6
function StringConcat() {
if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
throw MakeTypeError("called_on_null_or_undefined", ["String.prototype.concat"]);
}
var len = %_ArgumentsLength();
var this_as_string = TO_STRING_INLINE(this);
if (len === 1) {
return this_as_string + %_Arguments(0);
}
var parts = new InternalArray(len + 1);
parts[0] = this_as_string;
for (var i = 0; i < len; i++) {
var part = %_Arguments(i);
parts[i + 1] = TO_STRING_INLINE(part);
}
return %StringBuilderConcat(parts, len + 1, "");
}
So, internally they optimize it by creating an InternalArray (the parts variable), which is then filled. The StringBuilderConcat function is called with these parts. It's fast because the StringBuilderConcat function is some heavily optimized C++ code. It's too long to quote here, but search in the runtime.cc file for RUNTIME_FUNCTION(MaybeObject*, Runtime_StringBuilderConcat) to see the code.
Firefox is fast because it uses something called Ropes (Ropes: an Alternative to Strings). A rope is basically just a DAG, where every Node is a string.
So for example, if you would do a = 'abc'.concat('def'), the newly created object would look like this. Of course this is not exactly how this looks like in memory, because you still need to have a field for the string type, length and maybe other.
a = {
nodeA: 'abc',
nodeB: 'def'
}
And b = a.concat('123')
b = {
nodeA: a, /* {
nodeA: 'abc',
nodeB: 'def'
} */
nodeB: '123'
}
So in the simplest case the VM has to do nearly no work. The only problem is that this slows down other operations on the resulting string a little bit. Also this of course reduces memory overhead.
On the other hand ['abc', 'def'].join('') would usually just allocate memory to lay out the new string flat in memory. (Maybe this should be optimized)
It appears Array.join() for the most part across browsers is faster.
Current gen Browsers
FF3 array.join is ~2x faster
Safari 3 array.join ~1.5x faster
Opera 9 += ~3x faster
ie6 array.join ~6x faster
ie7 array.join ~4x faster
Next gen browsers
FF3.1 += array.join equal in speed
Chrome += ~1.25x faster
IE8 array.join ~1.6x faster
Webkit array.join ~2x faster
Test results here: http://www.learningjquery.com/2009/03/43439-reasons-to-use-append-correctly
My feeling is that since there's no "nice" way to do a feature check to see whether string concatenation is slow, and because Array.join() is not terrible in non-IE browsers, using the array approach is good because your pages will behave pretty well everywhere and you won't have a mess to maintain (and you can avoid browser sniffing).
Now if you're implementing a framework or a library and not just a feature or two on a site, then more fine tuning might be called for.
Finally, I suggest going back to google and reading some of the results you get from a search for "javascript string concatenation performance." I found several good ones and I only got through half the first SRP. Read the comments on the blog posts too because often you pick up interesting links there.