What's the Difference Between JavaScript String.substring() and String.slice() Methods?

Syntactically, both String.prototype.substring() and String.prototype.slice() are similar:

str.substring(indexStart);
str.substring(indexStart, indexEnd);

str.slice(indexStart);
str.slice(indexStart, indexEnd);

And, they both behave in the same way when:

  • indexStart equals indexEnd — they both return an empty string;
  • indexEnd is omitted — they both extract characters to the end of the string;
  • Either argument is greater than the string's length — the string's length will be used in both cases.

However, they have some minor differences in terms of when:

  1. indexStart Is Greater Than indexEnd;
  2. Either Argument Is Negative or NaN.

Differences When indexStart Is Greater Than indexEnd

When indexStart > indexEnd, then:

  • substring() will swap the two arguments;
  • slice() will return an empty string.

For example:

const str = 'Designcise';

// substring() swaps the arguments:
console.log(str.substring(6, 2)); // 'sign'
// same as:
console.log(str.substring(2, 6)); // 'sign'

// slice() returns empty string:
console.log(str.slice(6, 2)); // ''

Differences When indexStart or indexEnd Is Negative or NaN

String.prototype.substring():

In case of substring(), if either, or both, arguments are negative or NaN, then it's treated as 0. For example:

const str = 'Designcise';

console.log(str.substring(-6, 2));  // 'De'
// same as:
console.log(str.substring(0, 2));  // 'De'

console.log(str.substring(-6, -2)); // ''
// same as:
console.log(str.substring(0, 0)); // ''

String.prototype.slice():

slice() treats NaN arguments as 0. For example:

const str = 'Designcise';

console.log(str.slice(NaN, 2));  // 'De'
// same as:
console.log(str.substring(0, 2));  // 'De'

When either, or both, arguments are negative, then it counts backwards from the end of the string to find the indexes.

If indexStart < 0, then the index is counted from the end of the string (i.e. the substring starts at max(indexStart + str.length, 0)). For example:

const str = 'Designcise';

console.log(str.slice(-4)); // 'cise'

// is equivalent to `max(-4 + str.length, 0) = 6`:
console.log(str.slice(6)); // 'cise'

Similarly, if indexEnd < 0, then the index is counted from the end of the string (i.e. the substring ends at max(indexEnd + str.length, 0)). For example:

const str = 'Designcise';

console.log(str.slice(2, -4)); // 'sign'

// is equivalent to `max(-4 + str.length, 0) = 6`:
console.log(str.slice(2, 6)); // 'sign'

You can, of course, have instances where both values are negative, for example:

const str = 'Designcise';

console.log(str.slice(-4, -2)); // 'ci'
// same as:
console.log(str.slice(6, 8)); // 'ci'
console.log(str.slice(-4, 8)); // 'ci'
console.log(str.slice(6, -2)); // 'ci'

However, if indexEnd <= indexStart after normalizing negative values (i.e. indexEnd represents a character that's before indexStart), then an empty string is returned. For example:

const str = 'Designcise';

console.log(str.slice(-4, -6)); // ''
// same as:
console.log(str.slice(6, 4)); // ''
const str = 'Designcise';

console.log(str.slice(-4, 2)); // ''
// same as:
console.log(str.slice(6, 2)); // ''

This post was published by Daniyal Hamid. Daniyal currently works as the Head of Engineering in Germany and has 20+ years of experience in software engineering, design and marketing. Please show your love and support by sharing this post.