298

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

Adrian Toman
  • 11,316
  • 5
  • 48
  • 62
DevelopingChris
  • 39,797
  • 30
  • 87
  • 118
  • 5
    Yes, the y are immutable and you need a "string builder" of some sort. Read this http://blog.codeeffects.com/Article/String-Builder-In-Java-Script or this http://www.codeproject.com/KB/scripting/stringbuilder.aspx – Kizz Jul 14 '11 at 20:14
  • 4
    Interesting, those examples contradict my findings in my answer. – Ruan Mendes Feb 23 '12 at 19:29

10 Answers10

352

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 data-count="1000">

  // 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('');
    }
  }

  // @benchmark StringBuilderArrayIndex - same string
  {
    const sb = new StringBuilderArrayIndex();

    for (let i = 0; i < APPEND_COUNT; i++) {
      sb.append(STR)
    }
    sb.toString();
  }

  // @benchmark StringBuilderStringAppend - same string
  {
    const sb = new StringBuilderStringAppend();

    for (let i = 0; i < APPEND_COUNT; i++) {
      sb.append(STR)
    }
    sb.toString();
  }
  
  // @benchmark StringBuilderStringAppend - random strings
  {
    const sb = new StringBuilderStringAppend();

    for (let i = 0; i < APPEND_COUNT; i++) {
      sb.append(randomStrings[i])
    }
    sb.toString();
  }
  
  // @benchmark StringBuilderArrayIndex - random strings
  {
    const sb = new StringBuilderArrayIndex();

    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.

Results on different browsers on a Mac in July 2023

Chrome 114.0.5735.198

Chrome 114.0.5735.198 (July 2023)

Brave

Brave

Firefox

Firefox

Safari

Safari

Opera

Opera

Ruan Mendes
  • 90,375
  • 31
  • 153
  • 217
  • @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). http://jsperf.com/join-vs-str-concat-large-array – Roy Tinker Jul 22 '13 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 http://jsperf.com/string-concat-without-sringbuilder/7 Feel free to create new test cases, but creating the array is part of the test itself – Ruan Mendes Jul 22 '13 at 18:11
  • @JuanMendes My goal was to narrow the test case to a strict comparison of `join` vs string concatenation, hence building the array prior to the test. I don't think that's cheating if that goal is understood (and `join` enumerates the array internally, so it's not cheating to omit a `for` loop from the `join` test, either). – Roy Tinker Jul 22 '13 at 18:18
  • 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 – Ruan Mendes Jul 22 '13 at 18:20
  • 1
    @JuanMendes - Ok, point taken. My test assumes the array already exists, which you can't assume when evaluating string builders. – Roy Tinker Jul 22 '13 at 18:22
  • I suspect that the reason *concat* is sometimes faster is that strings actually *are* mutable. In other words, if you only have a single reference to a given string, that string may be mutated rather than having to allocate a new string and copy the old one. Python does this by calling `realloc`, meaning that the optimization only happens when there is enough free space after the end of the allocation. If `realloc` has to copy the old string, there's no advantage to this method, and a builder is more optimal. – Gabe Nov 10 '14 at 19:20
  • @JuanMendes Are you not the same person who said "The question is about whether a string builder is needed"? And if your runtime is doing a realloc behind the scenes, a string builder isn't needed. – Gabe May 02 '15 at 03:31
  • @Gabe I created a test where I'm concatenating random strings and I keep a reference to each of the growing strings in the concatenation. I think it's not a valid test case because JS doesn't intern concatenated strings (does it?), so it may be that there's still only one reference to the new strings created during concatenation. I'd love to hear your feedback, [here is the test](http://jsperf.com/string-concat-without-sringbuilder/11) and here's a reference about [string interning in JS](http://stackoverflow.com/questions/5276915/do-common-javascript-implementations-use-string-interning) – Ruan Mendes May 02 '15 at 12:37
  • 1
    `Exploder` with D. I see what you did here. :D – Calmarius Aug 24 '18 at 06:52
  • @JuanMendes Please update your post regarding Opera: as with Opera 57 (and probably many older releases), straight concat is the fastest option and using an array is the slowest option. – Cœur Jan 08 '19 at 04:37
  • @Cœur Feel free to udpate it yourself as long as you include sources! – Ruan Mendes Jan 09 '19 at 13:46
  • My only source is the one that you already provide for your tests, [Browserscope](http://www.browserscope.org/user/tests/table/ahBzfnVhLXByb2ZpbGVyLWhycg0LEgRUZXN0GKrynRUM). – Cœur Jan 09 '19 at 14:07
  • That's great, go ahead and modify it. Stack Overflow is a collaborative place. As long as you are not modifying the intent of the post – Ruan Mendes Jan 09 '19 at 14:16
45

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
  • 270,886
  • 87
  • 504
  • 504
minty
  • 22,235
  • 40
  • 89
  • 106
  • 8
    Link to an appropriate section of the rhino book: http://books.google.com/books?id=VOS6IlCsuU4C&pg=PA47&vq=strings+immutable&dq=javascript&client=firefox-a&source=gbs_search_s&cad=0 – baudtack Jun 06 '09 at 05:55
  • 142
    The Rhino book quote (and thus this answer) is _wrong_ here. In JavaScript strings are primitive value types and _not_ objects [(spec)](http://es5.github.io/#x4.3.16). 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. – Benjamin Gruenbaum Sep 16 '13 at 22:20
  • 3
    @BenjaminGruenbaum So, could it be said that JS strings *are* immutable, but it's *because* they're primitives instead of objects? – Ryan Kinal Sep 17 '13 at 15:31
  • 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);` – Benjamin Gruenbaum Sep 17 '13 at 16:08
  • 3
    @BenjaminGruenbaum - is this also true for strings created with `new String("...")`? I found [this](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String#Distinction_between_string_primitives_and_String_objects) interesting: "Note that JavaScript distinguishes between String objects and primitive string values. (The same is true of Boolean and Numbers.)" – Vidar S. Ramdal Feb 21 '14 at 13:23
  • 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 http://es5.github.io/#x15.5.2.1 . About how things convert to objects see http://es5.github.io/#x9.9 – Benjamin Gruenbaum Feb 21 '14 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. – Jisang Yoo Apr 02 '14 at 21:28
  • 1
    @JisangYoo: Note that in Python (and Ruby and various other languages), it's not just a difference in terminology; `str` really is a subclass of `object`; it has all of the standard `object` methods and works with all of the generic top-level functions like `dir`. There is no concept of "primitive value types" in Python. Of course all of that is untrue in JS. – abarnert Nov 10 '14 at 19:55
  • Related information about [javaScript strings](http://stackoverflow.com/questions/7675127/is-string-a-primitive-type-or-object-in-javascript) – patrik Mar 04 '15 at 07:20
  • 1
    The book's excerpt contradicts itself. Exhibit 1: "Strings are assigned by reference...". Exhibit 2: "when an object is assigned by reference...". The book then wrongly implies both strings and objects are assigned by reference! It's a bad book. – revelt Aug 02 '18 at 11:25
  • 1
    Please edit this answer as proposed here: https://meta.stackoverflow.com/a/255470/3995261 – YakovL Nov 10 '18 at 13:59
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.
Community
  • 1
  • 1
Katinka Hesselink
  • 3,961
  • 4
  • 20
  • 26
  • Would be the same if you do `var immutableString = "Hello"; immutableString="world"` ? I mean assign a totally new value to the variable –  Sep 07 '21 at 01:47
  • Sure, you can do that. – Katinka Hesselink Sep 07 '21 at 07: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... –  Sep 07 '21 at 16:15
  • 3
    You're replacing it with a new memory field. The old string gets discarded. – Katinka Hesselink Sep 09 '21 at 14:48
24

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
  • 70,800
  • 18
  • 132
  • 147
Ash
  • 60,973
  • 31
  • 151
  • 169
  • I know there isn't a stringBuilder, msAjax has one, and I was just pondering whether or not its useful – DevelopingChris Sep 09 '08 at 03:56
  • 7
    What does this have to do with strings being immutable or not? – baudtack Jun 06 '09 at 05:56
  • 4
    @docgnome: Because strings are immutable, string concatenation requires creating more objects than the Array.join approach – Ruan Mendes Jan 17 '11 at 20:37
  • 9
    According to Juan's test above, string concatenation is actually faster in both IE and Chrome, while slower in Firefox. – Bill Yang Feb 21 '12 at 18:36
  • 10
    Consider updating your answer, it may have been true a long time ago, but it's not anymore. See http://jsperf.com/string-concat-without-sringbuilder/5 – Ruan Mendes May 20 '14 at 16:21
5

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.

zhanziyang
  • 71
  • 1
  • 3
  • Javascript String objects are immutable https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures – prasun Oct 14 '15 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? – zhanziyang Oct 15 '15 at 04: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 – prasun Oct 15 '15 at 04:29
  • 7
    `new String` generates a mutable wrapper around an immutable string – tomdemuyt Dec 02 '15 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. – Mark Reed Jan 30 '18 at 16:29
  • As much as this is true, it's not really relevant to the question. Sure, there's a mutable String wrapper for the immutable string, which you can modify, but once it gets unboxed again, any modifications are gone. This is misleading at worst, useless information at best The question is about whether we modify the actual string, not add useless properties to a wrapper object. – Ruan Mendes Mar 31 '22 at 15:36
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
GibboK
  • 71,848
  • 143
  • 435
  • 658
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.

BirgerH
  • 748
  • 7
  • 8
1

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

revelt
  • 2,312
  • 1
  • 25
  • 37
  • 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](https://en.wikipedia.org/wiki/Evaluation_strategy#Call_by_value) is an interesting read on this topic. – ToolmakerSteve Oct 15 '19 at 17:45
0

JavaScript strings are indeed immutable.

JC Grubbs
  • 39,191
  • 28
  • 66
  • 75
0

Strings in Javascript are immutable

Glenn Slaven
  • 33,720
  • 26
  • 113
  • 165