There are two types (actually more, but for the problem at hand only these two are important):
-
flat strings are immutable arrays of characters
-
cons strings are pairs of strings, result of concatenation.
If you concat a and b you get a cons-string (a, b)
that represents result of concatenation. If you later concat d
to that you get another cons-string ((a, b), d)
.
Indexing into such a "tree-like" string is not O(1) so to make it faster V8 flattens the string when you index: copies all characters into a flat string.
Now what happens if you mix building strings are indexing into them? You and up flattening string again and again copying the same prefix over and over which leads to a worse complexity.
Advice here is simple: don't mix building strings and indexing into them or use a true mutable data structure for that like an array.
Some VMs (e.g. SpiderMonkey AFAIK) solve this problem by utilising a combination of mutable backing stores and string slices (strings that represent a range inside another string). When you do c = a + b
and backing store for a
has enough space left to contain a + b
the following happens: result of concatenation c
inherits this backing store and a
is rewritten to become a slice that points to the prefix of c
.