
JavaScript String Manipulation: substring() vs. substr()

There are quite a few different methods available in JavaScript for string manipulation (I talk briefly about some of the more confusing ones in my article here), with String.prototype.substring() being one of the most commonly used methods for extracting a section of a string. Understanding how to use substring() effectively and being aware of how it differs from the similarly named String.prototype.substr() is crucial for any JavaScript developer.
What is prototype.substring()?
As simply as possible: String.prototype.substring() is a JavaScript method which returns a substring, produced by passing in an original string, and two indices which define where the substring should begin and end.
For example, if you wanted a string made up of the third, fourth, and fifth letters of 'abcdefg', your use would look something like this: ('abcdefg').substring(2, 5);
This is a non‑destructive method, which means that your original string remains unchanged.
Syntax of prototype.substring()
The substring() method takes two arguments:
indexStart: the zero‑based index of the start of the substring.indexEnd(optional): the zero‑based index before which to end the extraction. If omitted,substring()extracts characters to the end of the string.
string.substring(indexStart[, indexEnd])How Does It Work?
The substring() method extracts characters from indexStart up to, but not including, indexEnd. If indexEnd is less than indexStart, substring() will swap the two arguments around (effectively working backwards).
An Example in Code
const str = 'Kavanagh';const sub1 = str.substring(0, 3);console.log(sub1); //=> 'Kav'Here, we're using substring() to extract the characters between the first (index 0) and fourth (index 3) of the string 'Kavanagh'. It's worth repeating with this example: indexEnd is the index before which extraction stops. That is to say: the fourth index (or character) in the string is not included.
Special Cases
If either argument is less than 0 or is NaN, it is treated as if it were 0.
Negative indexStart value
So if we set the indexStart to ‑1 for example, the result would be the same as before:
const str = 'Kavanagh';const sub1 = str.substring(-1, 3);console.log(sub1); //=> 'Kav'Negative indexEnd value
However, if indexEnd is ‑1, then we receive back an empty string, as both indexStart and indexEnd are treated as 0.
const str = 'Kavanagh';const sub1 = str.substring(0, -1);console.log(sub1); //=> ''Negative indexStart and indexEnd values
If both indexStart and indexEnd are negative then ‑ again ‑ they will both be treated as zero and ‑ again ‑ you'll get back an empty string because there's no character to extract between 0 and 0:
const str = 'Kavanagh';const sub1 = str.substring(-4, -1);console.log(sub1); //=> ''When the indexStart is greater than indexEnd
This is where developers can get tripped up when using substring(). When the indexStart is greater than indexEnd, the substring() method swaps the two arguments and then proceeds with the operation.
Here's a code example to illustrate this behaviour:
const str = 'Kavanagh';const sub1 = str.substring(5, 1);console.log(sub1); //=> 'avan'Here, because 5 is greater than 1, substring() swaps the values around to behave as though the call was str.substring(1, 5). As a result, it extracts the characters starting at index 1 and ending before index 5, which gives us the output 'avan'.
The Difference Between substring() and substr()
It is fair to say that misuse and misunderstanding between substring() and substr() is one of the most common issues I see in code reviews, particularly with less‑experienced developers.
The substr() method was never formally part of the ECMAScript (ES) specification and is not part of the core JavaScript standard. Although it has been widely supported by browsers as far back (and probably even further than Internet Explorer 6), its future is not guaranteed, and it may be removed in future versions of JavaScript engines or browsers. The MDN Web Docs advise against using substr() and suggest using substring() or slice() instead.
Nevertheless, substring() often gets confused with substr(). Whilst both methods seem to perform a relatively similar function (and have very similar names), there are fundamental differences which make them incompatible with one another.
Parameters
substr()takes astartindex and alengthof characters to extract.substring()takes astartindex and anendindex.
Negative Indexes
substr()supports negative index values, referring to the end of the string.substring()interprets negative values as 0.
In Code
Here's what those differences look like in code:
const str = 'Kavanagh';console.log(str.substring(2, 5)); //=> 'van'console.log(str.substr(2, 5)); //=> 'vanag'console.log(str.substring(-5, 2)); //=> 'Ka'console.log(str.substr(-5, 2)); //=> 'an'Issues with Confusion
As you can see, although similar, substring() and substr() fundamentally are not the same. This confusion can lead to several issues:
Unexpected Results
: Misinterpretingsubstr()'s length parameter as an index can result in strings of unexpected length.Negative Indexes
: Using negative indexes withsubstring()expectingsubstr()‑like behaviour will not yield the intended substring.Deprecation Concerns
: Becausesubstr()is considered a legacy function, it is not recommended for use in new JavaScript code. It's not part of the core JavaScript standard (ECMAScript) and could (and probably will) be removed from browsers in the future.
Wrapping‑Up
It is a common misunderstanding that substring() and substr() may seem interchangeable. Their differences are significant.
String.prototype.substring() is a reliable and essential method for string operations in JavaScript. Its behaviour is distinct from String.prototype.substr(), which is a legacy method with different parameters and potential for deprecation. Understanding these differences is paramount to avoiding bugs and maintaining clear, future‑proof code.
When in doubt, prefer substring() for its consistency with the standard and its predictable, standardised behaviour. When refactoring legacy code, don't simply swap substr() out for substring() without paying close attention to the parameters.
Categories:
Related Articles

Solving the LeetCode Two Sum Problem Using JavaScript. 
Mastering JavaScript's slice(). Mastering JavaScript's
slice()Advanced Sass: Loops. Advanced Sass: Loops

How to Choose a React Developer. How to Choose a React Developer

Introducing Seeded Randomisation into an SSR Gatsby Project. Introducing Seeded Randomisation into an SSR Gatsby Project

Integrating CMSes with HTML, CSS, and JavaScript. Integrating CMSes with HTML, CSS, and JavaScript

What are Higher‑Order Components in React? What are Higher‑Order Components in React?

301 vs. 307 Redirects. 301 vs. 307 Redirects

The Safest Way to Test for NaN. The Safest Way to Test for
NaN
Controlled vs. Uncontrolled Components in React. Controlled vs. Uncontrolled Components in React

Best Practices for Vue Router in Large Applications. Best Practices for Vue Router in Large Applications

String to Integer (atoi): Decoding Strings in JavaScript. String to Integer (atoi): Decoding Strings in JavaScript