How to hold strings by reference in JavaScript
JavaScript strings are held by value, and behave exactly like any other primitive. This means that each time we slice a string, we actually occupy extra memory slots. Although this trade off makes coding more comfortable and a whole lot easier, it’s very inefficient in memory:
edit: After investigating around I have found out that some browsers use string interning which means that the browser will hold a pool of string literals with references to its sub strings, so memory management might result in something different, however; this is not officially a part of TC39 specification and might vary across different browsers / different operations / different function calls.
Moreover, once you create a string you create it for good. If you would like to change one of its characters you would have to redefine it all over again:
I would like to introduce you to the ArrayBuffer class. The ArrayBuffer provides a way to allocate memory and control it directly. It has been around for a long time roughly since 2012 and but it’s highly overlooked. Originally it was born of the need to have an efficient way to handle binary data in WebGL, but it can be used for any purpose regardless.
Assuming that we would like to create a string that is made out of UTF-16 code units, which includes the most common characters that can be represented by a single 16-bit value, we would have to allocate 2 bytes per character:
Other set of classes that go hand in hand with the ArrayBuffer are TypedArrays. A TypedArray object describes an array-like view of an underlying binary data buffer. In our case, since we would like to represent a UTF-16 string, we would use the Uint16Array TypedArray, which consists of unsigned 16-bit integers:
Since we’re sharing the same ArrayBuffer object, any change that we would make in one TypedArray would be reflected in the other:
To finalize things up we can convert the byte code into a string using the String.fromCharCode()
function:
All of the above can be put together into a couple of classes that will wrap that logic and even add some handy utility functions:
So going back to the very first code snippet in this article, this is how the memory would look like when splitting a "hello world"
string:
Note that ArrayBuffers can also be used to reference other types of primitive values like numbers or booleans, it doesn’t necessarily apply to string. As long as you’re aware of how much memory a given primitive value occupies you’re good to go:
ArrayBuffers are efficient but be wary:
- Any of the prototype methods which are normally available on primitive values will not be available.
- Their lengths are pre-defined in advance and cannot be changed or exceeded.
As long as you’re aware of these limitations and use ArrayBuffers responsibly, you’re good to go.