Differentiate Between the 3 Methods for Working With Substrings in JavaScript
substr, substring, slice
As at 2021, there are 3 methods in JavaScript to extract substrings from a string, which are:
String.prototype.substr
String.prototype.substring
String.prototype.slice
You can easily get them mixed up, so let's take a closer look at each one of these methods.
TLDR;
.substr
should not be used..substring
treats negative numbers as index0
..slice
treats negative values as a hint to count backwards from the end of the string to find the indexes.- they all treat
NaN
as index0
. - if the first argument (a) is greater than the second argument (b):
.substr
returns b number of characters starting from index a.substring
swaps the two arguments making b the first argument and a the second argument.slice
returns an empty string
Let's go!
What is a substring?
Well, it is basically a portion of a string. For example, "methods for" is a substring of the title of this post because it contains some of the characters present in the original string (which is the title of this post).
.substr(startIndex, length)
The .substr
method returns n
characters from the start
index of the original string.
The n
in this case is the length
passed in.
Just think of it like this, start
from this index and return length
number of characters.
Don't yet understand? Hopefully these examples can help:
// "m in marco" index is 0
// "p in polo" index is 6
// "p in peach" index is 11
// "i in ice" index is 17
// "t in tea" index is 21
// the number of characters in the string is 24.
'marco polo peach ice tea'.substr(0, 5) // returns "marco"
'marco polo peach ice tea'.substr(6, 4) // returns "polo"
'marco polo peach ice tea'.substr(11, 5) // returns "peach"
'marco polo peach ice tea'.substr(17, 3) // returns "ice"
'marco polo peach ice tea'.substr(21, 3) // returns "tea"
The first line returns 5 characters from the 0th index.
The second line returns 4 characters from the 6th index.
The third line returns 5 characters from the 11th index.
And so on.
Here are some things you may want to know:
- If the
start
argument is omitted, the original string will be returned.
'this is the way'.substr() // returns "this is the way"
- If the
length
argument is omitted orundefined
, the rest of the string will be returned.
'this is the way'.substr(6) // returns "s the way"
- Any argument value that is greater than the length of the string, is treated as if it were the value of the length of the string.
'this is the way'.substr(12, Infinity) // returns "way"
- If
start
is a negative number, the index starts counting from the end of the string (reversed).
'this is the way'.substr(-3) // returns "way"
'this is the way'.substr(-10, 6) // returns "is the"
- If
length
is a negative number, it is treated as0
.
'this is the way'.substr(3, -1) // returns ""
NaN
is treated as0
when passed in as an argument.
'this is the way'.substr('bar', 7) // returns "this is"
According to the MDN docs, .substr
should be avoided when possible.
Furthermore, substr() is considered a legacy feature in ECMAScript and could be removed from future versions, so it is best to avoid using it if possible.
.substring(startIndex, endIndex)
The .substring
method returns characters from the start
index, up to the n
th character in the string.
The n
in this case is the endIndex
passed in.
Just think of it like this, return the characters in the string that is between start
and end
, but with start
included.
For example;
// index of "m in marco" is 0
// index of "p in polo" is 6
// index of "p in peach" is 11
// index of "i in ice" is 17
// index of "t in tea" is 21
// the number of characters in the string is 24.
'marco polo peach ice tea'.substring(0, 5) // returns "marco"
'marco polo peach ice tea'.substring(6, 10) // returns "polo"
'marco polo peach ice tea'.substring(11, 16) // returns "peach"
'marco polo peach ice tea'.substring(17, 20) // returns "ice"
'marco polo peach ice tea'.substring(21, 24) // returns "tea"
You can understand it in different ways:
The first line returns characters from the 0th index up to the 5th character* in the string.
The first line returns characters from the 0th index up to the character just before the 5th index.
The first line returns the characters between the 0th index and the 5th index, but with the character at the 0th index included.
I know, I know, might need some getting used to.
As with .substr
, the following holds:
- If the
start
argument is omitted, the original string will be returned.
'this is the way'.substring() // returns "this is the way"
- If the
end
argument is omitted, the rest of the string will be returned.
'this is the way'.substring(6) // returns "s the way"
- Any argument value that is
NaN
is treated as if it were0
.
'this is the way'.substring('foo', 6) // returns "this i"
- Any argument value that is greater than the length of the string, is treated as if it were the value of the length of the string.
'this is the way'.substring(12, Infinity) // returns "way"
Other things to know about:
- Any argument value that is less than
0
, is treated as if it were0
.
'to infinity and beyond'.substring(-Infinity, 11) // returns "to infinity"
- If the first argument is greater than the second argument, the values will be swapped with each other.
'to infinity and beyond'.substring(11, 3) // returns "infinity"
But wait! I wasn't expecting that. Yeah, be on the lookout.
if (firstArg > secondArg) {
;[firstArg, secondArg] = [secondArg, firstArg]
}
.slice(startIndex, endIndex)
The .substring
and .slice
methods are almost identical, but with two subtle differences between them.
.substring
swaps its two arguments if the first argument is greater than the second argument. While.slice
returns an empty string if this is the case.
// substring
'should have gone for the head'.substring(11, 3) // returns "uld have"
// slice
'should have gone for the head'.slice(11, 3) // returns ""
.substring
treats negative numbers as0
. While.slice
counts backward from the end of the string to find the indexes.
// substring
'should have gone for the head'.substring(-11, 24) // returns "should have gone for the"
// slice
'should have gone for the head'.slice(-11, 24) // returns "or the"
Differences and Similarities.
Now let's look at the differences between these methods and their similarities.
Differences
.substring
and.slice
both expect an index as the second argument while.substr
expects the number of characters to be returned as the second argument..substring
swaps its two arguments if the first argument is greater than the second argument. While.slice
returns an empty string if this is the case..substring
treats negative numbers as0
. While.substr
and.slice
treats negative numbers as a hint to count from the end of the string.
Similarities
- They all return consecutive characters between two positions of the original string.
- They are all non-destructive, that is, they do not mutate the original string but return the resulting value.
- If the first argument is omitted, the original string will be returned.
- They all treat
NaN
as0
.
Wrap Up
Apart from directly extracting a substring once, a good use case for .substring
and .slice
is having a cursor move through a string and extract values conditionally from the string.
You just need to keep track of the cursor and the different start
positions, then easily extract characters between the sets of indexes.
Using .substr
for that will result in performing an extra logic so you can get the length
to use as a second argument.
The browser support for these methods is very green but, using .substr
in IE8 and earlier has a caveat. Luckily, there is a polyfill for that.
Cover Photo by Karolina Grabowska from Pexels.