Notes · Archive
evergreen
Even faster String.prototype.trim() implementation in JavaScript
A faster, non-regex String.prototype.trim() — later adopted into early JS frameworks.
Cite this
Mangalapilly, Y. J. (2009, July). Even faster String.prototype.trim() implementation in JavaScript. Saṃhitā Notes. https://yesudeep.com/blog/even-faster-string-trim/ @online{mangalapilly2009even,
author = {Yesudeep Jose Mangalapilly},
title = {Even faster String.prototype.trim() implementation in JavaScript},
journal = {Sa\d{m}hit\=a Notes},
year = {2009},
month = {July},
url = {https://yesudeep.com/blog/even-faster-string-trim/},
urldate = {2009-07-31},
} Yesudeep Jose Mangalapilly. “Even faster String.prototype.trim() implementation in JavaScript.” Saṃhitā Notes, 2009. https://yesudeep.com/blog/even-faster-string-trim/. TY - ELEC
AU - Mangalapilly, Yesudeep Jose
TI - Even faster String.prototype.trim() implementation in JavaScript
T2 - Saṃhitā Notes
PY - 2009
UR - https://yesudeep.com/blog/even-faster-string-trim/
Y2 - 2009-07-31
ER - Note
Originally published on my old WordPress blog in 2009. Preserved here with its original date; the original is still online. Annotations from 2026, because this one has aged into a lesson about benchmarks rather than about trimming: (1) native String.prototype.trim had shipped in Firefox 3.5 a month before this post and was standardized in ES5 that December — today no hand-rolled trim competes with the builtin. (2) trim14 and trim16 below contain a genuine bug: charAt where charCodeAt is required, so a string with leading-but-not-trailing whitespace comes back untrimmed — the single 27 KB test string never took that branch, which is the methodology lesson. (3) The code credited to Nikolay Nemshilov at the end is, character for character, Steven Levithan's trim12 (his own 2008 hybrid). (4) The sparse-array "hash table" trick was already the slow path on the very engine benchmarked — V8 stores dense arrays contiguously and demotes sparse ones to dictionary mode. The 2009 text below is otherwise untouched.
I'll begin with a quote:
Some people, when confronted with a problem, think “I know, I'll use regular expressions.” Now they have two problems.
Those are the words of one of my favorite hackers, Jamie Zawinski.
Most JavaScript trim() implementations you see around are based on regular expressions and work fairly well for infrequent use on short strings. Some of them are well-crafted, but I think plain old loops can do better. Back in 2007, Steve Levithan covered some really fast implementations of trim() followed by a few more articles by others, including Luca Guidi.
Many of these implementations were based on regular expressions and a few were exceptionally brilliant. But did they answer the question of speed? In one of the comments posted on Steve Levithan's blog there was a little gem written by Michael Lee Finney, and truly that little function proves to be the fastest of all of the implementations mentioned in those articles. Finney claimed to better previous implementations by 20x, and he was right. His code totally smoked the regular-expression based implementations.
The Details
Arrays in JavaScript behave like hash-tables and hence exhibit the property of being sparse. Finney's implementation exploits this feature of JavaScript to its fullest. Here is a lookup table he uses to mark characters as whitespace:
String.whiteSpace = [];
String.whiteSpace[0x0009] = true;
String.whiteSpace[0x000a] = true;
String.whiteSpace[0x000b] = true;
String.whiteSpace[0x000c] = true;
String.whiteSpace[0x000d] = true;
String.whiteSpace[0x0020] = true;
String.whiteSpace[0x0085] = true;
String.whiteSpace[0x00a0] = true;
String.whiteSpace[0x1680] = true;
String.whiteSpace[0x180e] = true;
String.whiteSpace[0x2000] = true;
String.whiteSpace[0x2001] = true;
String.whiteSpace[0x2002] = true;
String.whiteSpace[0x2003] = true;
String.whiteSpace[0x2004] = true;
String.whiteSpace[0x2005] = true;
String.whiteSpace[0x2006] = true;
String.whiteSpace[0x2007] = true;
String.whiteSpace[0x2008] = true;
String.whiteSpace[0x2009] = true;
String.whiteSpace[0x200a] = true;
String.whiteSpace[0x200b] = true;
String.whiteSpace[0x2028] = true;
String.whiteSpace[0x2029] = true;
String.whiteSpace[0x202f] = true;
String.whiteSpace[0x205f] = true;
String.whiteSpace[0x3000] = true;The implementation of the function simply indexes into this array to check whether any character encountered while traversing the string is valid whitespace. The function runs noticeably faster. Here's his code:
function trim14(str) {
var len = str.length, whiteSpace, i;
if (!len) {
return str;
}
whiteSpace = String.whiteSpace;
if (len && whiteSpace[str.charCodeAt(len-1)])
{
do{
--len;
}
while (len && whiteSpace[str.charCodeAt(len - 1)]);
if (len && whiteSpace[str.charCodeAt(0)]){
i = 1;
while (i < len && whiteSpace[str.charCodeAt(i)]){
++i;
}
}
return str.substring(i, len);
}
if (len && whiteSpace[str.charCodeAt(0)]) {
i = 1;
while (i < len && whiteSpace[str.charAt(i)]){
++i;
}
return str.substring(i, len);
}
return str;
};Neatly written. I did spot a few unnecessary checks and removed them for a retest. The following code shaved off a couple more milliseconds sometimes.
function trim16(str) {
var len = str.length, whiteSpace = String.whiteSpace, i = 0;
if (len) {
if (whiteSpace[str.charCodeAt(len - 1)]) {
// Remove from the end.
while (--len && whiteSpace[str.charCodeAt(len - 1)]);
// Remove from the beginning.
if (len && whiteSpace[str.charCodeAt(0)]){
i = 1;
while (i < len && whiteSpace[str.charCodeAt(i)]){
++i; // Keep this here.
}
}
return str.substring(i, len);
}
// Remove from the beginning.
if (whiteSpace[str.charCodeAt(0)]) {
i = 1;
while (i < len && whiteSpace[str.charAt(i)]){
++i;
}
return str.substring(i, len);
}
}
return str;
};Can this be any better and faster?
I think it can. There's repetition in the code that could be removed as well. I rewrote the function to use Finney's look up table and ended up with this:
function trim17(str){
var len = str.length;
if (len){
var whiteSpace = String.whiteSpace, i = 0;
while (whiteSpace[str.charCodeAt(--len)]);
if (++len){
while (whiteSpace[str.charCodeAt(i)]){ ++i; }
}
str = str.substring(i, len);
}
return str;
}Guess what? Here are the benchmark results from a Chrome developer build running on Linux with 100000 iterations:
Original length: 27663
trim10: 363ms (length: 27656)
trim11: 4668ms (length: 27656)
trim12: 4671ms (length: 27656)
trim13: 322ms (length: 27656)
trim14: 197ms (length: 27656)
trim15: 195ms (length: 27656)
trim16: 197ms (length: 27656)
trim17: 187ms (length: 27656)
trim17 is indeed even faster and shorter.
Updates
Nikolay “MadRabbit” V. Nemshilov, the author of RightJS (a beautiful JavaScript library), provides another implementation of the function:
function trim19(str){
var str = str.replace(/^\s\s*/, ''),
ws = /\s/,
i = str.length;
while (ws.test(str.charAt(--i)));
return str.slice(0, i + 1);
}which is faster than many other regexp-based implementations.
I have included that in the benchmark as well and generated a couple of graphs.
The shape of the result is the whole point. The two regexp-based implementations (trim11, trim12) take over twenty times as long as the non-regexp ones — the bars below are the verbatim Linux/Chrome numbers above, redrawn natively for this archive (the original 2009 screenshots are long gone). Smaller is better; trim17, the shortest, is also the fastest.
trim() implementations, 100,000 iterations on a Chrome developer build (Linux). The two regexp variants dwarf the rest; the accented trim17 is the fastest and shortest. Verbatim figures from the run above.The regexp bars are so tall that the differences among the fast implementations vanish underneath them — which is exactly the lesson. On their own, trim13 through trim17 are separated by mere milliseconds; the real decision was made the moment you chose not to reach for a regular expression.
Feel free to use my code under the terms of the MIT license. As usual, suggestions and constructive criticism are most welcome.
How to cite
Mangalapilly, Y. J. (2009, July). Even faster String.prototype.trim() implementation in JavaScript. Saṃhitā Notes. https://yesudeep.com/blog/even-faster-string-trim/ @online{mangalapilly2009even,
author = {Yesudeep Jose Mangalapilly},
title = {Even faster String.prototype.trim() implementation in JavaScript},
journal = {Sa\d{m}hit\=a Notes},
year = {2009},
month = {July},
url = {https://yesudeep.com/blog/even-faster-string-trim/},
urldate = {2009-07-31},
} Yesudeep Jose Mangalapilly. “Even faster String.prototype.trim() implementation in JavaScript.” Saṃhitā Notes, 2009. https://yesudeep.com/blog/even-faster-string-trim/. TY - ELEC
AU - Mangalapilly, Yesudeep Jose
TI - Even faster String.prototype.trim() implementation in JavaScript
T2 - Saṃhitā Notes
PY - 2009
UR - https://yesudeep.com/blog/even-faster-string-trim/
Y2 - 2009-07-31
ER - Webmentions
Annotations
Thank you — your note is held for review and will appear once approved.
Thank you — your note is published.
Please sign in below to leave a note.
