Skip to main content
Refine AI
JavaScript Substring Method
12 min read

JavaScript Substring Method

This article was last updated on December 31, 2024, to include common mistakes to avoid when working with JavaScript’s substring() method, such as skipping the endIndex or misunderstanding negative indices. A Frequently Asked Questions section has also been added to clarify common issues and provide practical examples.

Introduction

This post is about how to effectively use the String.prototype.substring() method in JavaScript. We go through a few examples to understand how it works, play around to observe a few patterns and explore the quirks.

What is JavaScript substring()?

JavaScript substring() is a String method that is typically used to extract and store part of a string. During the extraction, the original string remains intact, and the target part is returned as a new string.

String.prototype.substring() is handy for producing substrings of characters from a point to another of a parent string. The substring may be from the beginning, in-between specified indexes or from the tail.

In this post, we perform some exercise with the JS substring() method by going through a few examples. We start by getting familiar with the method signature of String.prototype.substring(), the required argument (startIndex), the additional argument (endIndex) and understand which ones to use when. We then go ahead and see examples of extracting a number of characters after a starting index, characters from a range between two indexes, characters from the beginning and from the tail. While doing so, we elaborate on a few patterns of extraction from a parent string: namely, that of extracting a tail after first n characters, another of producing a substring composed of first n characters and that of extracting last n characters.

In the later half of the post, we discuss some of the nuances associated with startIndex and endIndex values. In the end, we point out how JavaScript String.prototype.substring() compares to String.prototype.slice() as well as the deprecated method String.prototype.substr().

Step by step, we'll cover the following topics:

JavaScript substring() Method

Array.prototype.substring() takes two possible arguments: a startIndex and an endIndex. startIndex is mandatory, while endIndex can be passed to explicitly indicate the termination of extraction.

Array.prototype.substring() Method Signature

We can call substring()with the below possible signatures:

substring(startIndex);
substring(startIndex, endIndex);

startIndex represents the point where substring extraction kicks off. The value at startIndex is inclusive, i.e. it is included in the returned substring. The endIndex denotes termination of the extraction. However, the value at endIndex is excluded. This means that we end extraction at the character before endIndex.

Let's see this in action with some examples.

JavaScript substring() with startIndex Only

With only startIndex passed to substring(), we get a substring that begins at startIndex and spans to the end of the parent string:

const mnemonic =
"Please, send, cats, monkeys, and, zebras, in, large, cages, make, sure, padlocked.";
console.log(mnemonic.substring(14)); // "cats, monkeys, and, zebras, in, large, cages, make, sure, padlocked."

As we can figure out from the string above, chopping starts at startIndex and we get the tail from that point on.

Extract Tail After First n Characters - JavaScript substring()

Since we are using a zero-based index for startIndex, the first startIndex number of characters are represented by the index just before startIndex. And since the value at startIndex is included in the extracted tail, the following pattern emerges where with startIndex = n, we get the tail after first n characters in the parent string:

const mnemonic =
"Please, send, cats, monkeys, and, zebras, in, large, cages, make, sure, padlocked.";

// Extract characters after first 14
console.log(mnemonic.substring(14)); // "cats, monkeys, and, zebras, in, large, cages, make, sure, padlocked."

// Extract tail after first 20
console.log(mnemonic.substring(20)); // "monkeys, and, zebras, in, large, cages, make, sure, padlocked."

// Extract tails after 29
console.log(mnemonic.substring(29)); // "and, zebras, in, large, cages, make, sure, padlocked."

// Extract after 72
console.log(mnemonic.substring(72)); // "padlocked."

JavaScript String.prototype.substring() - Extract a Substring Between Two Points

When we pass both the startIndex and endIndex, we get a substring of characters in startIndex <= str < endIndex range:

const mnemonic =
"Please, send, cats, monkeys, and, zebras, in, large, cages, make, sure, padlocked.";
console.log(mnemonic.substring(14, 27)); // "cats, monkeys"

This means, now we end up with a substring that ends before the endIndex.

Extract First n Characters - JavaScript substring()

When we need a substring from the start of the parent string, the startIndex should be 0:

const mnemonic =
"Please, send, cats, monkeys, and, zebras, in, large, cages, make, sure, padlocked.";
console.log(mnemonic.substring(0, 27)); // "Please send, cats, monkeys"

With startIndex = 0 and endIndex = n, we get the first n characters from the parent string:

const mnemonic =
"Please, send, cats, monkeys, and, zebras, in, large, cages, make, sure, padlocked.";

// First 12 characters
console.log(mnemonic.substring(0, 12)); // "Please, send"

// First 18 characters
console.log(mnemonic.substring(0, 18)); // "Please, send, cats"

// First 27 characters
console.log(mnemonic.substring(0, 27)); // "Please, send, cats, monkeys"

// First 70 characters
console.log(mnemonic.substring(0, 70)); // "Please, send, cats, monkeys, and, zebras, in, large, cages, make, sure"

Extract Last n Characters Using String.prototype.substring()

We can get the last n characters by leveraging the caller length in startIndex:

const mnemonic =
"Please, send, cats, monkeys, and, zebras, in, large, cages, make, sure, padlocked.";

// Last 9 characters
console.log(mnemonic.substring(mnemonic.length - 9)); // "padlocked."

// Last 15 characters
console.log(mnemonic.substring(mnemonic.length - 15)); // "sure, padlocked."

The way the above pattern works is that mnemonic.length sets the startIndex just outside the parent string. Moving startIndex back by -n repositions the extraction entry to the tail by n. And since we are not passing endIndex, extraction continues till the end of the string. So, we get the last n characters.

Nuances of startIndex and endIndex in JavaScript substring()

Other quirks of using substring() include cases when startIndex is greater than endIndex and when either of startIndex and endIndex or both are negative. Let's look at them one by one.

JavaScript substring() with startIndex > endIndex

When startIndex is greater than endIndex JavaScript substring() swaps the indexes to produce the substring:

const mnemonic =
"Please, send, cats, monkeys, and, zebras, in, large, cages, make, sure, padlocked.";

// Swaps to mnemonic.substring(0, 6)
console.log(mnemonic.substring(6, 0)); // "Please"

JavaScripty substring() with Negative startIndex and endIndex

A negative value of startIndex or endIndex sets the respective value to 0:

const mnemonic =
"Please, send, cats, monkeys, and, zebras, in, large, cages, make, sure, padlocked.";

// Same as mnemonic.substring(0, 6)
console.log(mnemonic.substring(-1, 6)); // "Please"

// Same as mnemonic.substring(0, 0)
console.log(mnemonic.substring(-1, -6)); // ""

// // Same as mnemonic.substring(12, 0)
console.log(mnemonic.substring(12, -6)); // "Please, send"

In the last invocation above, swapping between startIndex and endIndex is also involved since 12 > 0. The call is equivalent to mnemonic.substring(0, 12);.

String.prototype.substring() Comparison

In this section, we briefly discuss how JavaScript substring() method differs from slice() and substr(), which are also similar Stringextraction methods.

JavaScript substring() vs slice()

String.prototype.substring() and String.prototype.slice() both implement string extraction almost identically. However, there are some subtle differences in their implementations.

For example, swapping of arguments -- which we saw above in substring() -- doesn't take place in slice() when startIndex > endIndex:

const mnemonic =
"Please, send, cats, monkeys, and, zebras, in, large, cages, make, sure, padlocked.";

// Swaps to mnemonic.substring(0, 6)
console.log(mnemonic.substring(6, 0)); // "Please"

// Doesn't swap. Returns empty string, meaning non-existent characters
console.log(mnemonic.slice(6, 0)); // ""

With slice(), startIndex > endIndex returns an empty string.

Another difference is when either or both of startIndex and endIndex are negative. In general, when they are negative, slice() wraps startIndex and endIndex to the tail by traversing backward from the last character. And then if startIndex < endIndex, slicing works:

const mnemonic = "Please, send, cats, monkeys, and, zebras, in, large, cages, make, sure, padlocked.";

// Zeroes and swaps to mnemonic.substring(0, 18)
console.log(mnemonic.substring(18, -12)); // "Please, send, cats"

// Doesn't swap, -12 finds index from tail. String is long enough, slicing works
console.log(mnemonic.slice(20, -12)); // "monkeys, and, zebras, in, large, cages, make, sure"
.........

console.log(mnemonic.substring(-12, 18)); // "Please, send, cats"

// Doesn't swap. -12 finds index from tail, but startIndex > endIndex. Returns non-existent string
console.log(mnemonic.slice(-12, 18)); // ""
.........

console.log(mnemonic.substring(-16, -12)); // ""

// Both startIndex and endIndex finds index from tail. startIndex < endIndex. Slicing works
console.log(mnemonic.slice(-16, -12)); // "sure"

JavaScript substring() vs substr()

JavaScript substr() is also another method for extracting substrings from a parent string. However, it is a legacy String method that accepts the length of the target substring, instead of an index for extraction exit.

In other words, with substr(), we get indicate the length of the substring:

const mnemonic =
"Please, send, cats, monkeys, and, zebras, in, large, cages, make, sure, padlocked.";

console.log(mnemonic.substring(10, 12)); // "nd"

// Gets 12 characters starting from startIndex onwards
console.log(mnemonic.substr(10, 12)); // "nd, cats, mo"

Also, as with slice(), with a negative startIndex, substr() counts backwards from the last character:

const mnemonic =
"Please, send, cats, monkeys, and, zebras, in, large, cages, make, sure, padlocked.";

console.log(mnemonic.substring(-10, 12)); // "Please, send"

// startIndex wraps to 10 items from tail. Gets 10 characters from tail, doesn't have the other 2
console.log(mnemonic.substr(-10, 12)); // "padlocked."

In this case, only 10 characters are picked because 2 out of targeted 12 are not available in the last 10 characters.

Common Mistakes When Working With substring()

Over the years, I have encountered a few mistakes developers make using the substring() method. Let me outline them below.

Skipping endIndex

A lot of us forget that the endIndex is optional. If you don’t pass it, substring() just extracts everything from the startIndex to the end of the string. That’s OK most of the time, but if you accidentally leave it out when you meant to extract a certain range, it may mess things up.

const text = "Hello, World!";
console.log(text.substring(7)); // "World!"
console.log(text.substring(7, 12)); // "World"

Before using substring(), I double-check whether I really need the endIndex or not.

const text = "Hello, World!";
console.log(text.substring(-5)); // "Hello, World!" (negative becomes 0)
console.log(text.slice(-5)); // "orld!"

What I do now: In cases when I need negative indexing, I stick with slice().

Reversed Indices Confusion

If the value of startIndex is greater than the value of endIndex, substring() will swap them. If you’re not paying attention, it can lead to unexpected behavior.

const text = "Hello, World!";
console.log(text.substring(12, 7)); // "World" (indices are swapped)
console.log(text.slice(12, 7)); // "" (slice doesn't swap, just returns an empty string)

What I do now: Always ensure that startIndex <= endIndex whenever using substring().

Frequently Asked Questions

What if startIndex equals endIndex?

You will get an empty string because substring() does not include the endIndex.

const text = "Hello, World!";
console.log(text.substring(5, 5)); // ""

Can substring() change the original string?

Nope - it’s non-destructive, simply returning the new string and leaving the original alone.

const text = "Hello, World!";
const result = text.substring(7);
console.log(result); // "World!"
console.log(text); // "Hello, World!"

What if the indices are out of range?

If the indices exceed the length of the string, substring() will simply stop at the string’s end.

const text = "Hello";
console.log(text.substring(2, 10)); // "llo"
console.log(text.substring(10, 20)); // ""

How does substring() differ from slice()?

Both do similar things, but here’s the difference:

  • substring() swaps indices if startIndex > endIndex. slice() doesn’t.
  • slice() supports negative indices; substring() doesn’t.
const text = "Hello, World!";
console.log(text.substring(7, 12)); // "World"
console.log(text.slice(-5)); // "orld!"

Negative Indices Misconception

Here’s a big one: substring() doesn’t handle negative numbers. If you pass in a negative startIndex or endIndex, it will just treat them as 0. I’ve seen people confuse this with slice(), which actually supports negative indices.

Summary

In this post, we exercised the use of JavaScript substring() with some basic examples. We first learned that substring() is a String extraction method that returns a target portion from a parent string. We got familiar with the required startIndex argument whose value is included in the extracted substring, and also with the endIndex argument which indicates termination of the string.

We then explored with examples how to use substring() with only startIndex and discovered how to extract the tail after first n characters of the parent string. We also saw how to extract a substring between two points with startIndex and endIndex passed. We found out another pattern where we can produce the first n characters by passing 0 as startIndex and n as the endIndex. We also figured out how we can extract the last n characters using caller length - n.

Towards the later half, we touch based on some other nuances of using startIndex and endIndex values. In the end, we compared and discussed how substring() differs in implementation from JavaScript slice() and substr().