String.prototype.slice()
The one to reach for by default. JavaScript has three substring methods; this is the only one that handles negative indices, and the only one whose name matches the array method that behaves the same way.
Demo
The end index is exclusive, so slice(1, 3) gives two characters, not three — the same convention as every other range in the language. Negative indices count from the right, which is what makes slice(0, -1) the idiomatic way to drop the last character. The backwards case is the important contrast with substring: given a start after the end, slice returns an empty string, while substring silently swaps the two arguments and returns a result. Indices past the end are clamped rather than throwing.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| start | number | yes | Index to start at. Negative counts back from the end, so -3 is the third character from the right. |
| end | number | no (length) | Index to stop BEFORE. Also accepts negatives. Omitted, the slice runs to the end of the string. |
Return value
string — The characters from start up to but NOT including end. An empty string when the range is backwards or out of bounds — it never throws.
Common patterns
const short = title.slice(0, 80);
const withoutComma = line.slice(0, -1);
const ext = name.slice(name.lastIndexOf('.') + 1);
Examples
Pitfalls
'abcdef'.slice(3, 1)
'abcdef'.slice(Math.min(a, b), Math.max(a, b))
'abcdef'.substring(-3)
'abcdef'.slice(-3)
'\u{1F600}ab'.slice(0, 1).length
[...'\u{1F600}ab'].slice(0, 1).join('')
let s = 'abc'; s.slice(1); s
let s = 'abc'; s = s.slice(1); s
When to use
- Any substring extraction — this is the sensible default
- Taking characters from the end with a negative index
- Truncating to a maximum length without a bounds check
- Matching the behaviour of Array.prototype.slice
- You want the arguments auto-ordered → substring, though that is rarely a feature
- You want a length rather than an end index → the deprecated substr, or slice(i, i + n)
- The text may contain emoji and you are truncating → spread to an array first
- You want to split on a delimiter → split
Notes
FAQ
slice, almost always. substring differs only in swapping backwards ranges and clamping negatives to zero, neither of which is usually wanted. substr takes a LENGTH rather than an end index and is deprecated Annex B — it still works everywhere but should not appear in new code.
'abcdef'.slice(1, 3); // 'bc' 'abcdef'.substring(1, 3); // 'bc' — same here 'abcdef'.substr(1, 3); // 'bcd' — length, not end