# JavaScript String Methods & Properties

Strings are everywhere: usernames, URLs, search boxes, API responses. JavaScript offers many built-in methods for working with them. In this guide, we explain every method the same way: **what it does, what it returns, a simple example, and the gotchas** that trip people up. Where a modern or better alternative exists, you will see it too.

## Two rules to remember first

**1\. Strings are immutable.** No method changes the original string. Each one **returns a new value**, so you must store or use the result.

```javascript
let name = "  Sam  ";
name.trim();             // returns "Sam", but `name` is unchanged
console.log(name);       // "  Sam  "

name = name.trim();      // store the result
console.log(name);       // "Sam"
```

**2\. Indexes start at 0.** In `"hello"`, `h` is index 0, `e` is index 1, and so on.

```plaintext
 h  e  l  l  o
 0  1  2  3  4
-5 -4 -3 -2 -1   (negative indexes work only in some methods)
```

## 1\. `.length`

Gives the number of characters in the string. It is a **property, not a method**, so there are no parentheses.

*   **Returns:** a number
    

```javascript
"hello".length;   // 5
"".length;        // 0
"a b".length;     // 3 (spaces count)
```

**Gotcha:** `.length` Counts UTF-16 units, so some emojis count as 2.

```javascript
"😀".length;          // 2
[..."😀"].length;     // 1 (spread counts real characters)
```

**Last character trick:** `str[str.length - 1]` or, better, `str.at(-1)` (see below).

## 2\. `.toUpperCase()` and `.toLowerCase()`

Change the case of every letter.

*   **Returns:** a new string
    

```javascript
"Hello World".toUpperCase();   // "HELLO WORLD"
"Hello World".toLowerCase();   // "hello world"
```

**Most common use:** case-insensitive comparison.

```javascript
const input = "YES";
if (input.toLowerCase() === "yes") {
  console.log("Confirmed");
}
```

**Gotcha:** some languages have special rules (for example, German `ß` becomes `SS`). For locale-aware work, use `.toLocaleUpperCase("tr")` or `.toLocaleLowerCase("tr")`.

## 3\. `.trim()`, `.trimStart()`, `.trimEnd()`

Remove whitespace (spaces, tabs, newlines).

*   **Returns:** a new string
    
*   `trim()` removes from both ends, `trimStart()` from the left, `trimEnd()` from the right.
    

```javascript
"  hello  ".trim();        // "hello"
"  hello  ".trimStart();   // "hello  "
"  hello  ".trimEnd();     // "  hello"
```

**It does not touch spaces in the middle:**

```javascript
"  a   b  ".trim();        // "a   b"
```

**Real use:** always trim user input before validating it.

```javascript
const email = "  sam@mail.com ".trim();
```

**Better than:** `str.replace(/^\s+|\s+$/g, "")`. Same result, much more readable.

## 4\. `.includes(search, position?)`

Checks whether a string contains another string.

*   **Returns:** `true` or `false`
    
*   Optional second argument: index to start searching from.
    
*   **Case-sensitive.**
    

```javascript
"JavaScript is fun".includes("Script");   // true
"JavaScript is fun".includes("script");   // false (case matters)
"hello".includes("l", 4);                 // false (starts looking at index 4)
```

**Case-insensitive version:**

```javascript
text.toLowerCase().includes(word.toLowerCase());
```

**Better than:** `str.indexOf("x") !== -1`. `includes()` says exactly what you mean.

**Gotcha:** passing a regular expression throws a `TypeError`. Use `.test()` or `.match()` for patterns.

## 5\. `.indexOf()` and `.lastIndexOf()`

Find **where** something is.

*   **Returns:** the index (number), or `-1` if not found.
    

```javascript
"banana".indexOf("a");        // 1  (first match)
"banana".lastIndexOf("a");    // 5  (last match)
"banana".indexOf("x");        // -1 (not found)
"banana".indexOf("a", 2);     // 3  (start searching from index 2)
```

**Use it when you need the position.** If you only need yes or no, use `includes()`.

**Gotcha:** `if (str.indexOf("a"))` is a bug. Index `0` is falsy. Always compare: `!== -1`.

## 6\. `.startsWith()` and `.endsWith()`

Check the beginning or end of a string.

*   **Returns:** `true` or `false`
    

```javascript
"https://site.com".startsWith("https");   // true
"photo.png".endsWith(".png");             // true
"photo.png".endsWith(".jpg");             // false
"hello".startsWith("ell", 1);             // true (start checking at index 1)
"hello".endsWith("hel", 3);               // true (treat string as only 3 long)
```

**Better than:** `str.indexOf("x") === 0` or `str.slice(-4) === ".png"`.

## 7\. `.charAt(index)`

Gets the character at a position.

*   **Returns:** a string with one character, or an **empty string** `""` if the index is out of range.
    

```javascript
"hello".charAt(1);    // "e"
"hello".charAt(10);   // ""
"hello".charAt(-1);   // "" (negative does not work here)
```

**Alternative:** bracket notation `"hello"[1]` gives the same result, but returns `undefined` when out of range.

## 8\. `.at(index)`

The modern way to get a character. It **supports negative indexes**, counting from the end.

*   **Returns:** a one-character string, or `undefined` if out of range.
    

```javascript
"hello".at(0);     // "h"
"hello".at(-1);    // "o"  (last character)
"hello".at(-2);    // "l"
"hello".at(10);    // undefined
```

**Why it is better:** compared to getting the last character.

```javascript
str.charAt(str.length - 1);   // old way
str[str.length - 1];          // old way
str.at(-1);                   // clean, modern
```

**Quick comparison**

| Method | Index `-1` | Out of range |
| --- | --- | --- |
| `charAt(i)` | `""` | `""` |
| `str[i]` | `undefined` | `undefined` |
| `at(i)` | last character | `undefined` |

## 9\. `.slice(start, end?)`

Extracts part of a string.

*   **Returns:** a new string
    
*   `end` that is **not included**. Negative numbers count from the end.
    

```javascript
"JavaScript".slice(0, 4);    // "Java"
"JavaScript".slice(4);       // "Script"  (to the end)
"JavaScript".slice(-6);      // "Script"  (last 6 characters)
"JavaScript".slice(0, -6);   // "Java"    (everything except the last 6)
```

## 10\. `.substring(start, end?)`

Similar to `slice()`, with two differences:

*   Negative numbers are treated as `0`.
    
*   If `start` is bigger than`end`, it **swaps** them.
    

```javascript
"JavaScript".substring(0, 4);    // "Java"
"JavaScript".substring(4, 0);    // "Java"  (swapped)
"JavaScript".substring(-3, 4);   // "Java"  (-3 becomes 0)
```

**Recommendation:** use `slice()`. It is more predictable. Also avoid the old `.substr()`. It is deprecated.

## 11\. `.split(separator, limit?)`

Breaks a string into an **array**.

*   **Returns:** an array of strings
    

```javascript
"a,b,c".split(",");          // ["a", "b", "c"]
"hello".split("");           // ["h", "e", "l", "l", "o"]
"one two  three".split(" "); // ["one", "two", "", "three"]
"a,b,c,d".split(",", 2);     // ["a", "b"]  (limit)
```

**Gotchas**

```javascript
"".split(",");      // [""]  (one empty string, not an empty array)
"".split("");       // []
```

**Common combo: reverse a string**

```javascript
"hello".split("").reverse().join("");   // "olleh"
```

**Safer for emojis:** `[..."hello"].reverse().join("")`.

## 12\. `.replace()` and `.replaceAll()`

Swap text for other text.

*   **Returns:** a new string
    

```javascript
"cat cat cat".replace("cat", "dog");      // "dog cat cat"  (first only)
"cat cat cat".replaceAll("cat", "dog");   // "dog dog dog"  (all)
```

**With a regex:**

```javascript
"cat cat".replace(/cat/g, "dog");         // "dog dog"
```

**Gotchas**

*   `replace()` With a plain string, it changes **only the first match**.
    
*   `replaceAll()` With a regex, it needs the `g` **flag**; otherwise it throws a `TypeError`.
    

**Better than:** `str.split("a").join("b")` or a global regex for simple text. `replaceAll()` is clearer.

## 13\. `.repeat(count)`

Repeats a string.

*   **Returns:** a new string
    

```javascript
"ha".repeat(3);     // "hahaha"
"-".repeat(20);     // "--------------------"
"x".repeat(0);      // ""
```

**Gotcha:** a negative number or `Infinity` throws a `RangeError`.

## 14\. `.padStart()` and `.padEnd()`

Add padding until the string reaches a length.

*   **Returns:** a new string
    
*   Syntax: `padStart(targetLength, padString = " ")`
    

```javascript
"5".padStart(3, "0");        // "005"
"42".padEnd(5, ".");         // "42..."
"abc".padStart(2, "0");      // "abc" (already long enough)
```

**Real uses:** time formatting and masking.

```javascript
String(7).padStart(2, "0");              // "07"
"4242".slice(-2).padStart(4, "*");       // "**42"
```

## 15\. `.charCodeAt()` and `.codePointAt()`

Get the numeric code of a character.

*   **Returns:** a number, or `NaN` if the index is out of range.
    

```javascript
"A".charCodeAt(0);       // 65
"a".charCodeAt(0);       // 97
"😀".charCodeAt(0);      // 55357 (only half of the emoji)
"😀".codePointAt(0);     // 128512 (the full emoji code)
```

**Rule:** prefer `codePointAt()` when emojis or rare symbols may appear. To go back to text: `String.fromCharCode(65)` gives `"A"`.

## 16\. `.localeCompare()`

Compares two strings in a language-aware way.

*   **Returns:** a **negative** number (comes first), `0` (same), or a **positive** number (comes after).
    

```javascript
"a".localeCompare("b");    // -1
"b".localeCompare("a");    // 1
"a".localeCompare("a");    // 0
```

**Sorting names correctly:**

```javascript
["Zoe", "adam", "Émile"].sort((a, b) => a.localeCompare(b));
// ["adam", "Émile", "Zoe"]
```

**Case-insensitive equality:**

```javascript
"Hello".localeCompare("hello", undefined, { sensitivity: "base" }) === 0;  // true
```

**Better than:** the default `.sort()`, which compares raw character codes and puts all capital letters first.

## 17\. `.match()` and `.matchAll()`

Find text using a regular expression.

```javascript
"id: 42, id: 99".match(/\d+/);     // ["42", index: 4, ...]  (first match with details)
"id: 42, id: 99".match(/\d+/g);    // ["42", "99"]           (all matches)
"abc".match(/\d+/);                // null (no match)
```

*   `match()` **returns:** an array, or `null` when nothing matches.
    
*   `matchAll()` **returns:** an iterator (needs the `g` flag) that gives full details for every match.
    

```javascript
for (const m of "a1 b2".matchAll(/([a-z])(\d)/g)) {
  console.log(m[1], m[2]);   // a 1, then b 2
}
```

**Tip:** if you only need yes or no, `/\d/.test(str)` is simpler and faster.

## 18\. `.concat()` (and why to skip it)

Joins strings.

*   **Returns:** a new string
    

```javascript
"Hello".concat(" ", "World");   // "Hello World"
```

**Better than it:** the `+` operator or **template literals**.

```javascript
const name = "Sam";
const age = 20;

`Hi ${name}, you are ${age}`;   // "Hi Sam, you are 20"
```

Template literals are easier to read and handle numbers and expressions for you.

## Optimized and modern choices (cheat sheet)

| Instead of | Use | Why |
| --- | --- | --- |
| `str.indexOf("x") !== -1` | `str.includes("x")` | Clearer |
| `str.indexOf("x") === 0` | `str.startsWith("x")` | Clearer |
| `str.charAt(str.length - 1)` | `str.at(-1)` | Short, supports negatives |
| `substr()` / `substring()` | `slice()` | Predictable, not deprecated |
| `a.concat(b)` or `a + " " + b` | `` `${a} ${b}` `` | Readable |
| `split("x").join("y")` | `replaceAll("x", "y")` | Clearer intent |
| regex \`/^\\s+ | \\s+$/g\` | `trim()` |
| default `.sort()` | `.sort((a, b) => a.localeCompare(b))` | Correct for letters and accents |
| `str.length` for emoji text | `[...str].length` | Counts real characters |
| `+=` in a huge loop | Push to an array, then `.join("")` | Faster for very large strings |

## Quick reference: what each method returns

| Method | Returns |
| --- | --- |
| `length` | number (property) |
| `toUpperCase()`, `toLowerCase()` | new string |
| `trim()`, `trimStart()`, `trimEnd()` | new string |
| `includes()`, `startsWith()`, `endsWith()` | boolean |
| `indexOf()`, `lastIndexOf()` | number (`-1` if not found) |
| `charAt()` | string (`""` if out of range) |
| `at()` | string (`undefined` if out of range) |
| `slice()`, `substring()` | new string |
| `split()` | array |
| `replace()`, `replaceAll()` | new string |
| `repeat()`, `padStart()`, `padEnd()` | new string |
| `charCodeAt()`, `codePointAt()` | number (`NaN` / `undefined` if out of range) |
| `localeCompare()` | negative, `0` or positive number |
| `match()` | array or `null` |
| `matchAll()` | iterator |

## Combining methods (chaining)

Because most methods return a string, you can chain them:

```javascript
const slug = "  Hello World From JS  "
  .trim()
  .toLowerCase()
  .replaceAll(" ", "-");

console.log(slug);   // "hello-world-from-js"
```

## Wrap up

*   Strings never change. Methods **return new values**.
    
*   Know the return type: boolean, number, string, or array.
    
*   Prefer the modern options: `at()`, `includes()`, `slice()`, `replaceAll()`, template literals.
    
*   Watch the gotchas: `-1` from `indexOf`, `null` from`match`, and emoji lengths.
    

Practice each method in the browser console, and the return values will stick quickly.
