305

Does javascript use immutable or mutable strings? Do I need a "string builder"?

Adrian Toman
11.5k6 gold badges50 silver badges63 bronze badges
asked Sep 9, 2008 at 3:44
2

10 Answers 10

360

They are immutable. You cannot change a character within a string with something like var myString = "abbdef"; myString[2] = 'c'. The string manipulation methods such as trim, slice return new strings.

In the same way, if you have two references to the same string, modifying one doesn't affect the other

let a = b = "hello";
a = a + " world";
// b is not affected

Myth Debunking - String concatenation is NOT slow

I've always heard what Ash mentioned in his answer (that using Array.join is faster for concatenation) so I wanted to test out the different methods of concatenating strings and abstracting the fastest way into a StringBuilder. I wrote some tests to see if this is true (it isn't!).

This was what I believed would be the fastest way, avoiding push and using an array to store the strings to then join them in the end.

class StringBuilderArrayIndex {
 array = [];
 index = 0;
 append(str) {
 this.array[this.index++] = str 
 }
 toString() {
 return this.array.join('')
 }
}

Some benchmarks

  • Read the test cases in the snippet below
  • Run the snippet
  • Press the benchmark button to run the tests and see results

I've created two types of tests

  • Using Array indexing to avoid Array.push, then using Array.join
  • Straight string concatenation

For each of those tests, I looped appending a constant value and a random string;

<script benchmark>
 // Number of times to loop through, appending random chars
 const APPEND_COUNT = 1000;
 const STR = 'Hot diggity dizzle';
 
 function generateRandomString() {
 const characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
 const length = Math.floor(Math.random() * 10) + 1; // Random length between 1 and 10
 let result = '';
 for (let i = 0; i < length; i++) {
 const randomIndex = Math.floor(Math.random() * characters.length);
 result += characters.charAt(randomIndex);
 }
 return result;
 }
 const randomStrings = Array.from({length: APPEND_COUNT}, generateRandomString);
 
 class StringBuilderStringAppend {
 str = '';
 append(str) {
 this.str += str;
 }
 toString() {
 return this.str;
 }
 }
 
 class StringBuilderArrayIndex {
 array = [];
 index = 0;
 append(str) {
 this.array[this.index] = str;
 this.index++;
 }
 toString() {
 return this.array.join('');
 }
 }
 // @group Same string 'Hot diggity dizzle'
 // @benchmark array push & join 
 {
 const sb = new StringBuilderArrayIndex();
 for (let i = 0; i < APPEND_COUNT; i++) {
 sb.append(STR)
 }
 sb.toString();
 }
 // @benchmark string concatenation
 {
 const sb = new StringBuilderStringAppend();
 for (let i = 0; i < APPEND_COUNT; i++) {
 sb.append(STR)
 }
 sb.toString();
 }
 // @group Random strings
 // @benchmark array push & join
 {
 const sb = new StringBuilderArrayIndex();
 for (let i = 0; i < APPEND_COUNT; i++) {
 sb.append(randomStrings[i])
 }
 sb.toString();
 }
 // @benchmark string concatenation
 {
 const sb = new StringBuilderStringAppend();
 for (let i = 0; i < APPEND_COUNT; i++) {
 sb.append(randomStrings[i])
 }
 sb.toString();
 }
 
 
</script>
<script src="https://cdn.jsdelivr.net/gh/silentmantra/benchmark/loader.js"></script>

Findings

Nowadays, all evergreen browsers handle string concatenation better, at least twice as fast.

i-12600k (added by Alexander Nenashev)

Chrome/117

--------------------------------------------------------------------
Same string 'Hot diggity dizzle'
 string concatenation  1.0x | x100000 224 232 254 266 275
 array push & join   3.2x | x100000 722 753 757 762 763
Random strings
 string concatenation  1.0x | x100000 261 268 270 273 279
 array push & join   5.4x |  x10000 142 147 148 155 166
--------------------------------------------------------------------
https://github.com/silentmantra/benchmark

Firefox/118

--------------------------------------------------------------------
Same string 'Hot diggity dizzle'
 string concatenation  1.0x | x100000 304 335 353 358 370
 array push & join   9.5x |  x10000 289 300 301 306 309
Random strings
 string concatenation  1.0x | x100000 334 337 345 349 377
 array push & join   5.1x |  x10000 169 176 176 176 180
--------------------------------------------------------------------
https://github.com/silentmantra/benchmark

Results below on a 2.4 GHz 8-Core i9 Mac on Oct 2023

Chrome

--------------------------------------------------------------------
Same string 'Hot diggity dizzle'
 string concatenation  1.0x | x100000 574 592 594 607 613
 array push & join   2.7x |  x10000 156 157 159 164 165
Random strings
 string concatenation  1.0x | x100000 657 663 669 675 680
 array push & join   4.3x |  x10000 283 285 295 298 311
--------------------------------------------------------------------
https://github.com/silentmantra/benchmark

Firefox

--------------------------------------------------------------------
Same string 'Hot diggity dizzle'
 string concatenation  1.0x | x100000 546 648 659 663 677
 array push & join   5.8x |  x10000 314 320 326 331 335
Random strings
 string concatenation  1.0x | x100000 647 739 764 765 804
 array push & join   2.9x |  x10000 187 188 199 219 231
--------------------------------------------------------------------
https://github.com/silentmantra/benchmark

Brave

--------------------------------------------------------------------
Same string 'Hot diggity dizzle'
 string concatenation  1.0x | x100000 566 571 572 579 600
 array push & join   2.5x |  x10000 144 145 159 162 166
Random strings
 string concatenation  1.0x | x100000 649 658 659 663 669
 array push & join   4.4x |  x10000 285 285 290 292 300
--------------------------------------------------------------------
https://github.com/silentmantra/benchmark `

Safari

--------------------------------------------------------------------
Same string 'Hot diggity dizzle'
 string concatenation  1.0x | x10000  76  77  77  79  82
 array push & join   2.2x | x10000 168 168 174 178 186
Random strings
 string concatenation  1.0x | x100000 878 884 889 892 903
 array push & join   2.3x |  x10000 199 200 202 202 204
--------------------------------------------------------------------
https://github.com/silentmantra/benchmark `

Opera

--------------------------------------------------------------------
Same string 'Hot diggity dizzle'
 string concatenation  1.0x | x100000 577 579 581 584 608
 array push & join   2.7x |  x10000 157 162 165 166 171
Random strings
 string concatenation  1.0x | x100000 688 694 740 750 781
 array push & join   4.2x |  x10000 291 315 316 317 379
--------------------------------------------------------------------
https://github.com/silentmantra/benchmark
answered Jan 17, 2011 at 21:03
13
  • @Juan, the link you asked us to visit concatenates a 112-char string 30 times. Here's another test that might help balance things - Array.join vs string concatenation on 20,000 different 1-char strings (join is much faster on IE/FF). jsperf.com/join-vs-str-concat-large-array Commented Jul 22, 2013 at 18:03
  • 1
    @RoyTinker Roy, oh Roy, your tests are cheating because you're creating the array in the setup of the test. Here's the real test using different characters jsperf.com/string-concat-without-sringbuilder/7 Feel free to create new test cases, but creating the array is part of the test itself Commented Jul 22, 2013 at 18:11
  • 2
    @RoyTinker Yes it is, any string builder will require building the array. The question is about whether a string builder is needed. If you already have the strings in an array, then it is not a valid test case for what we're discussing here Commented Jul 22, 2013 at 18:20
  • 1
    @JuanMendes - Ok, point taken. My test assumes the array already exists, which you can't assume when evaluating string builders. Commented Jul 22, 2013 at 18:22
  • 1
    Exploder with D. I see what you did here. :D Commented Aug 24, 2018 at 6:52
42

from the rhino book:

In JavaScript, strings are immutable objects, which means that the characters within them may not be changed and that any operations on strings actually create new strings. Strings are assigned by reference, not by value. In general, when an object is assigned by reference, a change made to the object through one reference will be visible through all other references to the object. Because strings cannot be changed, however, you can have multiple references to a string object and not worry that the string value will change without your knowing it

Benjamin Gruenbaum
277k90 gold badges524 silver badges524 bronze badges
answered Sep 9, 2008 at 3:51
11
  • 8
    Link to an appropriate section of the rhino book: books.google.com/… Commented Jun 6, 2009 at 5:55
  • 143
    The Rhino book quote (and thus this answer) is wrong here. In JavaScript strings are primitive value types and not objects (spec). In fact, as of ES5, they're one of the only 5 value types alongside null undefined number and boolean. Strings are assigned by value and not by reference and are passed as such. Thus, strings are not just immutable, they are a value. Changing the string "hello" to be "world" is like deciding that from now on the number 3 is the number 4... it makes no sense. Commented Sep 16, 2013 at 22:20
  • 12
    Yes, like my comment says strings are immutable, but they are not reference types nor they are objects - they are primitive value types. An easy way to see they're neither would be to try to add a property to a string and then read it: var a = "hello";var b=a;a.x=5;console.log(a.x,b.x); Commented Sep 17, 2013 at 16:08
  • 12
    @VidarS.Ramdal No, String objects created using the string constructor are wrappers around JavaScript string values. You can access the string value of the boxed type using the .valueOf() function - this is also true for Number objects and number values. It's important to note String objects created using new String are not actual strings but are wrappers or boxes around strings. See es5.github.io/#x15.5.2.1 . About how things convert to objects see es5.github.io/#x9.9 Commented Feb 21, 2014 at 13:43
  • 8
    As for why some people say strings are objects, they are probably coming from Python or Lisp or any other language where its spec use the word "object" to mean any kind of datum (even integers). They just need to read how the ECMA spec defines the word: "member of the type Object". Also, even the word "value" may mean different things according to specs of different languages. Commented Apr 2, 2014 at 21:28
26

Just to clarify for simple minds like mine (from MDN):

Immutables are the objects whose state cannot be changed once the object is created.

String and Numbers are Immutable.

Immutable means that:

You can make a variable name point to a new value, but the previous value is still held in memory. Hence the need for garbage collection.

var immutableString = "Hello";

// In the above code, a new object with string value is created.

immutableString = immutableString + "World";

// We are now appending "World" to the existing value.

This looks like we're mutating the string 'immutableString', but we're not. Instead:

On appending the "immutableString" with a string value, following events occur:

  1. Existing value of "immutableString" is retrieved
  2. "World" is appended to the existing value of "immutableString"
  3. The resultant value is then allocated to a new block of memory
  4. "immutableString" object now points to the newly created memory space
  5. Previously created memory space is now available for garbage collection.
answered Sep 9, 2018 at 8:06
4
  • Would be the same if you do var immutableString = "Hello"; immutableString="world" ? I mean assign a totally new value to the variable Commented Sep 7, 2021 at 1:47
  • Sure, you can do that. Commented Sep 7, 2021 at 7:15
  • thanks katinka but what I mean, would you be "mutating the string" if you assign a totally new value? Or applies the same you explained so well here? If It appears you are mutating it, but you´re not... Commented Sep 7, 2021 at 16:15
  • 3
    You're replacing it with a new memory field. The old string gets discarded. Commented Sep 9, 2021 at 14:48
23

Performance tip:

If you have to concatenate large strings, put the string parts into an array and use the Array.Join() method to get the overall string. This can be many times faster for concatenating a large number of strings.

There is no StringBuilder in JavaScript.

Cerbrus
73.3k19 gold badges137 silver badges151 bronze badges
answered Sep 9, 2008 at 3:55
5
  • I know there isn't a stringBuilder, msAjax has one, and I was just pondering whether or not its useful Commented Sep 9, 2008 at 3:56
  • 7
    What does this have to do with strings being immutable or not? Commented Jun 6, 2009 at 5:56
  • 4
    @docgnome: Because strings are immutable, string concatenation requires creating more objects than the Array.join approach Commented Jan 17, 2011 at 20:37
  • 9
    According to Juan's test above, string concatenation is actually faster in both IE and Chrome, while slower in Firefox. Commented Feb 21, 2012 at 18:36
  • 11
    Consider updating your answer, it may have been true a long time ago, but it's not anymore. See jsperf.com/string-concat-without-sringbuilder/5 Commented May 20, 2014 at 16:21
4

The string type value is immutable, but the String object, which is created by using the String() constructor, is mutable, because it is an object and you can add new properties to it.

> var str = new String("test")
undefined
> str
[String: 'test']
> str.newProp = "some value"
'some value'
> str
{ [String: 'test'] newProp: 'some value' }

Meanwhile, although you can add new properties, you can't change the already existing properties

A screenshot of a test in Chrome console

In conclusion, 1. all string type value (primitive type) is immutable. 2. The String object is mutable, but the string type value (primitive type) it contains is immutable.

answered Oct 14, 2015 at 14:18
6
  • 1
    Javascript String objects are immutable developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures Commented Oct 14, 2015 at 14:28
  • 1
    @prasun but in that page it says: "All types except objects define immutable values (values, which are incapable of being changed). " String objects are object. And how is it immutable if you can add new properties on it? Commented Oct 15, 2015 at 4:18
  • read the section "String Type". The Javascript's String link points to both primitive and Object and later it says "JavaScript strings are immutable". It looks like the document is not clear on this topic as it conflicts at two different notes Commented Oct 15, 2015 at 4:29
  • 8
    new String generates a mutable wrapper around an immutable string Commented Dec 2, 2015 at 14:29
  • 2
    It's very easy to test by running @zhanziyang's code above. You can totally add new properties to a String object (wrapper), meaning it is not immutable (by default; like any other object you can call Object.freeze on it to render it immutable). But a primitive string value type, whether contained in a String object wrapper or not, is always immutable. Commented Jan 30, 2018 at 16:29
3

Strings are immutable – they cannot change, we can only ever make new strings.

Example:

var str= "Immutable value"; // it is immutable
var other= statement.slice(2, 10); // new string
answered Aug 18, 2015 at 14:16
2

It's a late post, but I didn't find a good book quote among the answers.

Here's a definite except from a reliable book:

Strings are immutable in ECMAScript, meaning that once they are created, their values cannot change. To change the string held by a variable, the original string must be destroyed and the variable filled with another string containing a new value... —Professional JavaScript for Web Developers, 3rd Ed., p.43

Now, the answer which quotes Rhino book's excerpt is right about string immutability but wrong saying "Strings are assigned by reference, not by value." (probably they originally meant to put the words an opposite way).

The "reference/value" misconception is clarified in the "Professional JavaScript", chapter named "Primitive and Reference values":

The five primitive types...[are]: Undefined, Null, Boolean, Number, and String. These variables are said to be accessed by value, because you are manipulating the actual value stored in the variable. —Professional JavaScript for Web Developers, 3rd Ed., p.85

that's opposed to objects:

When you manipulate an object, you’re really working on a reference to that object rather than the actual object itself. For this reason, such values are said to be accessed by reference.—Professional JavaScript for Web Developers, 3rd Ed., p.85

answered Aug 2, 2018 at 11:41
1
  • FWIW: The Rhino book probably means that internally/implementation a string assignment is storing/copying a pointer (rather than copying the contents of the string). It doesn't look like an accident on their part, based on the text after that. But I agree: they misuse the term "by reference". Its not "by reference" just because the implementation passes pointers (for performance). Wiki - evaluation strategy is an interesting read on this topic. Commented Oct 15, 2019 at 17:45
1

Regarding your question (in your comment to Ash's response) about the StringBuilder in ASP.NET Ajax the experts seem to disagree on this one.

Christian Wenz says in his book Programming ASP.NET AJAX (O'Reilly) that "this approach does not have any measurable effect on memory (in fact, the implementation seems to be a tick slower than the standard approach)."

On the other hand Gallo et al say in their book ASP.NET AJAX in Action (Manning) that "When the number of strings to concatenate is larger, the string builder becomes an essential object to avoid huge performance drops."

I guess you'd need to do your own benchmarking and results might differ between browsers, too. However, even if it doesn't improve performance it might still be considered "useful" for programmers who are used to coding with StringBuilders in languages like C# or Java.

answered Sep 9, 2008 at 4:15
0

JavaScript strings are indeed immutable.

answered Sep 9, 2008 at 3:50
2
0

Strings in Javascript are immutable

answered Sep 9, 2008 at 3:50

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.