# String Polyfills and Common Interview Methods in JavaScript


Every JavaScript developer uses string methods. But do you know how they work? Building your own string utilities is more than an interview trick—it's how you truly understand JavaScript. And understanding them deeply makes you a better developer.

This is about how string methods work, why developers write polyfills, and how to ace those string manipulation interview questions.

* * *

## What Are String Methods?

String methods are functions that operate on strings. JavaScript has many built-in.

### Built-In String Methods

```javascript
const str = "Hello";

str.length           // 5
str.toUpperCase()    // "HELLO"
str.toLowerCase()    // "hello"
str.charAt(0)        // "H"
str.indexOf("e")     // 1
str.slice(0, 3)      // "Hel"
str.trim()           // Removes whitespace
```

These methods are available on every string. You've used them a thousand times.

### How Methods Work Conceptually

Every string has methods attached to it. When you call a method, JavaScript:

1. Takes the string as context (`this`)
2. Performs an operation
3. Returns a result

```plaintext
"hello".toUpperCase()
    ↓
1. this = "hello"
2. Convert all letters to uppercase
3. Return "HELLO"
```

### String Methods Are Just Functions

Under the hood, a string method is just a function that:
- Accesses the string's characters
- Does something with them
- Returns a result

Understanding this is key. Once you know how they work, you can build them.

* * *

## How Strings Actually Work

Before writing polyfills, understand how strings are structured.

### Strings Are Character Arrays

A string is a sequence of characters. You can access each character by index:

```javascript
const str = "Hello";

str[0]  // "H"
str[1]  // "e"
str[2]  // "l"
str[3]  // "l"
str[4]  // "o"
```

Each character has a position (index). Index 0 is the first character.

### Strings Have a Length

```javascript
const str = "Hello";
str.length  // 5
```

You can iterate through all characters:

```javascript
for (let i = 0; i < str.length; i++) {
  console.log(str[i]);
}

// Output:
// H
// e
// l
// l
// o
```

### Strings Are Immutable

Once created, you can't change a string. Methods return new strings:

```javascript
const original = "hello";
const upper = original.toUpperCase();

console.log(original); // "hello" (unchanged)
console.log(upper);    // "HELLO" (new string)
```

This is crucial for polyfills. You always create and return new strings.

* * *

## What Is a Polyfill?

A polyfill is code that replicates the functionality of a built-in method.

### Why Write Polyfills?

**Reason 1: Compatibility**

Some older browsers don't have newer methods. A polyfill provides that functionality:

```javascript
// Modern method
const trimmed = "  hello  ".trimStart();

// If the browser doesn't support trimStart(), use a polyfill
function trimStart(str) {
  let start = 0;
  while (str[start] === " ") {
    start++;
  }
  return str.slice(start);
}
```

**Reason 2: Understanding**

Writing a polyfill forces you to understand the logic. Interview questions often ask this.

**Reason 3: Custom Behavior**

Sometimes you need a slight variation:

```javascript
// Built-in returns the first occurrence
"hello".indexOf("l");  // 2

// Your polyfill could return all occurrences
function findAll(str, char) {
  const indices = [];
  for (let i = 0; i < str.length; i++) {
    if (str[i] === char) {
      indices.push(i);
    }
  }
  return indices;
}

findAll("hello", "l");  // [2, 3]
```

### Polyfill Structure

Every polyfill follows a pattern:

```javascript
function myPolyfill(input, ...args) {
  // 1. Validate input
  // 2. Initialize variables
  // 3. Iterate through the string (or other logic)
  // 4. Build the result
  // 5. Return the result
}
```

Let's build some.

* * *

## Implementing Simple String Utilities

### 1. Uppercase Polyfill

The logic: Convert each letter to uppercase.

```javascript
function toUpperCase(str) {
  let result = "";
  
  for (let i = 0; i < str.length; i++) {
    const char = str[i];
    // ASCII: lowercase a-z is 97-122, uppercase A-Z is 65-90
    // Difference is 32
    if (char >= "a" && char <= "z") {
      // Convert lowercase to uppercase
      result += String.fromCharCode(char.charCodeAt(0) - 32);
    } else {
      // Keep non-lowercase characters as-is
      result += char;
    }
  }
  
  return result;
}

toUpperCase("hello");     // "HELLO"
toUpperCase("Hello 123"); // "HELLO 123"
toUpperCase("WORLD");     // "WORLD"
```

**How it works:**
1. Loop through each character
2. Check if it's lowercase (a-z)
3. If yes, convert to uppercase using ASCII codes
4. If no, keep it as-is
5. Build and return the result

### 2. Lowercase Polyfill

Similar logic, opposite direction:

```javascript
function toLowerCase(str) {
  let result = "";
  
  for (let i = 0; i < str.length; i++) {
    const char = str[i];
    if (char >= "A" && char <= "Z") {
      // Convert uppercase to lowercase
      result += String.fromCharCode(char.charCodeAt(0) + 32);
    } else {
      result += char;
    }
  }
  
  return result;
}

toLowerCase("HELLO");     // "hello"
toLowerCase("Hello 123"); // "hello 123"
```

### 3. indexOf Polyfill

Find the first occurrence of a substring:

```javascript
function indexOf(str, searchStr, fromIndex = 0) {
  // Start from fromIndex (or 0 if not provided)
  for (let i = fromIndex; i < str.length; i++) {
    // Check if searchStr matches starting at position i
    let match = true;
    
    for (let j = 0; j < searchStr.length; j++) {
      if (str[i + j] !== searchStr[j]) {
        match = false;
        break;
      }
    }
    
    if (match) {
      return i; // Found it, return the index
    }
  }
  
  return -1; // Not found
}

indexOf("hello world", "o");      // 4
indexOf("hello world", "world");  // 6
indexOf("hello world", "xyz");    // -1
indexOf("hello hello", "ello", 2); // 5
```

**How it works:**
1. Loop through each position in the string
2. At each position, check if the substring matches
3. To check, compare character by character
4. If all match, return the index
5. If nothing matches, return -1

### 4. slice Polyfill

Extract a portion of the string:

```javascript
function slice(str, start = 0, end = str.length) {
  // Handle negative indices
  if (start < 0) start = Math.max(0, str.length + start);
  if (end < 0) end = Math.max(0, str.length + end);
  
  // Clamp to string boundaries
  start = Math.max(0, start);
  end = Math.min(str.length, end);
  
  let result = "";
  
  for (let i = start; i < end; i++) {
    result += str[i];
  }
  
  return result;
}

slice("hello", 0, 3);    // "hel"
slice("hello", 1);       // "ello"
slice("hello", -3);      // "llo"
slice("hello", -4, -1);  // "ell"
```

**How it works:**
1. Handle negative indices (count from the end)
2. Clamp values to string boundaries
3. Loop from start to end
4. Collect characters and return

### 5. includes Polyfill

Check if a string contains a substring:

```javascript
function includes(str, searchStr, position = 0) {
  // Start searching from position
  for (let i = position; i < str.length; i++) {
    // Try to match searchStr at position i
    let match = true;
    
    for (let j = 0; j < searchStr.length; j++) {
      if (str[i + j] !== searchStr[j]) {
        match = false;
        break;
      }
    }
    
    if (match) {
      return true;
    }
  }
  
  return false;
}

includes("hello world", "world");  // true
includes("hello world", "xyz");    // false
includes("hello world", "ell", 1); // true
```

### 6. split Polyfill

Split a string by a delimiter:

```javascript
function split(str, delimiter = "", limit) {
  const result = [];
  
  if (delimiter === "") {
    // Split into individual characters
    for (let i = 0; i < str.length; i++) {
      if (limit !== undefined && result.length >= limit) break;
      result.push(str[i]);
    }
  } else {
    let current = "";
    let i = 0;
    
    while (i < str.length) {
      // Check if delimiter matches at position i
      let match = true;
      for (let j = 0; j < delimiter.length; j++) {
        if (str[i + j] !== delimiter[j]) {
          match = false;
          break;
        }
      }
      
      if (match) {
        // Delimiter found
        result.push(current);
        if (limit !== undefined && result.length >= limit) {
          return result;
        }
        current = "";
        i += delimiter.length;
      } else {
        // Not a match, add character to current
        current += str[i];
        i++;
      }
    }
    
    result.push(current); // Add the last part
  }
  
  return result;
}

split("hello", "");        // ["h", "e", "l", "l", "o"]
split("a,b,c", ",");       // ["a", "b", "c"]
split("hello", "l");       // ["he", "", "o"]
split("a,b,c,d", ",", 2);  // ["a", "b"]
```

### 7. trim Polyfill

Remove whitespace from both ends:

```javascript
function trim(str) {
  let start = 0;
  let end = str.length - 1;
  
  // Find first non-whitespace character
  while (start <= end && str[start] === " ") {
    start++;
  }
  
  // Find last non-whitespace character
  while (end >= start && str[end] === " ") {
    end--;
  }
  
  // Return the trimmed string
  return str.slice(start, end + 1);
}

trim("  hello  ");     // "hello"
trim("hello");         // "hello"
trim("  ");            // ""
trim("  a b c  ");     // "a b c"
```

**How it works:**
1. Find the first non-space character from the left
2. Find the first non-space character from the right
3. Extract the substring between them

### 8. repeat Polyfill

Repeat a string multiple times:

```javascript
function repeat(str, count) {
  // Validate count
  if (count < 0 || count === Infinity) {
    throw new Error("Invalid count value");
  }
  
  let result = "";
  
  for (let i = 0; i < count; i++) {
    result += str;
  }
  
  return result;
}

repeat("ab", 3);    // "ababab"
repeat("hello", 2); // "hellohello"
repeat("x", 0);     // ""
```

### 9. replace Polyfill

Replace the first occurrence of a substring:

```javascript
function replace(str, searchStr, replaceStr) {
  // Find the first occurrence
  for (let i = 0; i < str.length; i++) {
    let match = true;
    
    // Check if searchStr matches at position i
    for (let j = 0; j < searchStr.length; j++) {
      if (str[i + j] !== searchStr[j]) {
        match = false;
        break;
      }
    }
    
    if (match) {
      // Found it, replace and return
      return str.slice(0, i) + replaceStr + str.slice(i + searchStr.length);
    }
  }
  
  // Not found, return original
  return str;
}

replace("hello world", "world", "universe"); // "hello universe"
replace("hello hello", "hello", "hi");       // "hi hello"
replace("hello", "xyz", "abc");              // "hello"
```

### 10. startsWith Polyfill

Check if a string starts with a substring:

```javascript
function startsWith(str, searchStr, position = 0) {
  // Check if str starts with searchStr from position
  for (let i = 0; i < searchStr.length; i++) {
    if (str[position + i] !== searchStr[i]) {
      return false;
    }
  }
  return true;
}

startsWith("hello", "he");      // true
startsWith("hello", "ell");     // false
startsWith("hello", "ell", 1);  // true
startsWith("hello", "");        // true
```

* * *

## String Processing Flow

Here's how string methods conceptually work:

```plaintext
Input String
    ↓
Method receives it
    ↓
Initialize result container
    ↓
Loop through characters
    ↓
    ├─ Evaluate each character
    ├─ Apply logic
    └─ Add to result
    ↓
Return new string
```

### Visual Example: indexOf

```plaintext
String: "hello world"
Search: "o"

Position 0: "h" vs "o" → No match
Position 1: "e" vs "o" → No match
Position 2: "l" vs "o" → No match
Position 3: "l" vs "o" → No match
Position 4: "o" vs "o" → MATCH!

Return: 4
```

### Visual Example: split

```plaintext
String: "a,b,c"
Delimiter: ","

Position 0: "a" → Not delimiter → Add to current
Position 1: "," → DELIMITER → Push "a" to result, reset
Position 2: "b" → Not delimiter → Add to current
Position 3: "," → DELIMITER → Push "b" to result, reset
Position 4: "c" → Not delimiter → Add to current
End of string → Push "c" to result

Result: ["a", "b", "c"]
```

* * *

## Common Interview String Problems

### Problem 1: Reverse a String

**Question:** Write a function to reverse a string.

```javascript
function reverseString(str) {
  let result = "";
  
  for (let i = str.length - 1; i >= 0; i--) {
    result += str[i];
  }
  
  return result;
}

reverseString("hello");  // "olleh"
reverseString("12345");  // "54321"
```

**Alternative:** Using built-in methods

```javascript
function reverseString(str) {
  return str.split("").reverse().join("");
}
```

### Problem 2: Check for Palindrome

**Question:** Check if a string is a palindrome.

```javascript
function isPalindrome(str) {
  // Remove spaces and convert to lowercase
  const clean = str.toLowerCase().replace(/\s/g, "");
  
  // Compare with reversed version
  let reversed = "";
  for (let i = clean.length - 1; i >= 0; i--) {
    reversed += clean[i];
  }
  
  return clean === reversed;
}

isPalindrome("racecar");      // true
isPalindrome("race car");     // true
isPalindrome("hello");        // false
```

**Alternative:** Two-pointer approach

```javascript
function isPalindrome(str) {
  const clean = str.toLowerCase().replace(/\s/g, "");
  let left = 0;
  let right = clean.length - 1;
  
  while (left < right) {
    if (clean[left] !== clean[right]) {
      return false;
    }
    left++;
    right--;
  }
  
  return true;
}
```

### Problem 3: Count Character Frequency

**Question:** Count how many times each character appears.

```javascript
function countCharacters(str) {
  const count = {};
  
  for (let i = 0; i < str.length; i++) {
    const char = str[i];
    count[char] = (count[char] || 0) + 1;
  }
  
  return count;
}

countCharacters("hello");
// { h: 1, e: 1, l: 2, o: 1 }

countCharacters("aabbcc");
// { a: 2, b: 2, c: 2 }
```

### Problem 4: Find the First Non-Repeated Character

**Question:** Find the first character that appears only once.

```javascript
function firstNonRepeated(str) {
  const count = {};
  
  // Count all characters
  for (let i = 0; i < str.length; i++) {
    const char = str[i];
    count[char] = (count[char] || 0) + 1;
  }
  
  // Find first character with count 1
  for (let i = 0; i < str.length; i++) {
    if (count[str[i]] === 1) {
      return str[i];
    }
  }
  
  return null;
}

firstNonRepeated("hello");      // "h"
firstNonRepeated("aabbcc");     // null
firstNonRepeated("aabbc");      // "c"
```

### Problem 5: Anagram Check

**Question:** Check if two strings are anagrams.

```javascript
function isAnagram(str1, str2) {
  // Remove spaces and convert to lowercase
  const clean1 = str1.toLowerCase().replace(/\s/g, "");
  const clean2 = str2.toLowerCase().replace(/\s/g, "");
  
  // Check if lengths match
  if (clean1.length !== clean2.length) {
    return false;
  }
  
  // Count characters in first string
  const count = {};
  for (let i = 0; i < clean1.length; i++) {
    const char = clean1[i];
    count[char] = (count[char] || 0) + 1;
  }
  
  // Check if characters match in second string
  for (let i = 0; i < clean2.length; i++) {
    const char = clean2[i];
    if (!count[char]) {
      return false;
    }
    count[char]--;
  }
  
  return true;
}

isAnagram("listen", "silent");      // true
isAnagram("hello", "world");        // false
isAnagram("abc", "bca");            // true
```

**Alternative:** Sorting approach

```javascript
function isAnagram(str1, str2) {
  const sort1 = str1.toLowerCase().replace(/\s/g, "").split("").sort().join("");
  const sort2 = str2.toLowerCase().replace(/\s/g, "").split("").sort().join("");
  return sort1 === sort2;
}
```

### Problem 6: String Compression

**Question:** Compress a string by replacing repeated characters with count.

```javascript
function compress(str) {
  if (str.length <= 2) return str;
  
  let result = "";
  let count = 1;
  
  for (let i = 0; i < str.length; i++) {
    // If next character is different or we're at the end
    if (str[i] !== str[i + 1]) {
      result += str[i] + count;
      count = 1;
    } else {
      count++;
    }
  }
  
  return result.length < str.length ? result : str;
}

compress("aabbcc");      // "a2b2c2"
compress("abcdef");      // "abcdef" (no compression)
compress("aaaa");        // "a4"
```

### Problem 7: Word Frequency

**Question:** Count how many times each word appears.

```javascript
function wordFrequency(str) {
  const words = str.toLowerCase().split(/\s+/);
  const count = {};
  
  for (let i = 0; i < words.length; i++) {
    const word = words[i];
    count[word] = (count[word] || 0) + 1;
  }
  
  return count;
}

wordFrequency("hello world hello");
// { hello: 2, world: 1 }
```

### Problem 8: Longest Substring Without Repeating Characters

**Question:** Find the length of the longest substring without repeating characters.

```javascript
function longestSubstring(str) {
  let maxLength = 0;
  let currentLength = 0;
  const charIndex = {};
  
  for (let i = 0; i < str.length; i++) {
    const char = str[i];
    
    // If character was seen and is in current window
    if (charIndex[char] !== undefined && charIndex[char] >= (i - currentLength)) {
      // Reset length
      currentLength = i - charIndex[char] - 1;
    } else {
      currentLength++;
    }
    
    charIndex[char] = i;
    maxLength = Math.max(maxLength, currentLength);
  }
  
  return maxLength;
}

longestSubstring("abcabcbb");  // 3 ("abc")
longestSubstring("bbbbb");     // 1 ("b")
longestSubstring("pwwkew");    // 3 ("kew")
```

* * *

## Why Understanding Built-In Behavior Matters

### Interview Preparation

Interviewers ask you to build string methods for a reason:

1. **It shows understanding** - You know how strings work at a fundamental level
2. **It tests logic** - You can think through problems algorithmically
3. **It reveals gaps** - Edge cases in your implementation show what you miss
4. **It's practical** - You can debug better when things go wrong

### Real-World Application

Understanding how methods work helps you:

1. **Debug faster** - When something goes wrong, you understand why
2. **Optimize** - You know the cost of operations
3. **Choose wisely** - Pick the right method for the job
4. **Extend functionality** - Build custom methods when needed

### Edge Cases Show Deep Understanding

```javascript
// A shallow implementation
function includes(str, search) {
  return str.indexOf(search) !== -1;
}

// A deep implementation handles edge cases
function includes(str, search, position = 0) {
  if (search === "") return true;         // Empty string is always included
  if (position >= str.length) return false; // Position out of bounds
  if (search.length > str.length) return false; // Search longer than string
  
  // ... actual logic
}
```

The difference is understanding the edge cases.

* * *

## Polyfill Behavior Representation

```plaintext
Built-in Method
    ↓
Input Validation
    ↓
    ├─ Check arguments
    ├─ Handle defaults
    └─ Validate types
    ↓
Core Logic
    ↓
    ├─ Loop through string
    ├─ Apply transformations
    └─ Build result
    ↓
Result Assembly
    ↓
    ├─ Combine parts
    ├─ Format output
    └─ Handle edge cases
    ↓
Return Result
```

Every polyfill follows this pattern.

* * *

## String Method Comparison Table

| Method | Purpose | Input | Output |
| --- | --- | --- | --- |
| toUpperCase | Convert to uppercase | string | string |
| toLowerCase | Convert to lowercase | string | string |
| indexOf | Find first occurrence | string, search, position | number |
| slice | Extract substring | string, start, end | string |
| includes | Check if contains | string, search, position | boolean |
| split | Split by delimiter | string, delimiter, limit | array |
| trim | Remove whitespace | string | string |
| repeat | Repeat string | string, count | string |
| replace | Replace occurrence | string, search, replace | string |
| startsWith | Check start | string, search, position | boolean |

* * *

## Practice Assignment

**1. Implement charAt:**

```javascript
function charAt(str, index) {
  // Return the character at the given index
  // Return empty string if out of bounds
}

charAt("hello", 0);  // "h"
charAt("hello", 5);  // ""
```

**2. Implement lastIndexOf:**

```javascript
function lastIndexOf(str, searchStr) {
  // Find the LAST occurrence of searchStr
  // Return -1 if not found
}

lastIndexOf("hello", "l");     // 3
lastIndexOf("hello", "hello"); // 0
```

**3. Implement endsWith:**

```javascript
function endsWith(str, searchStr, length) {
  // Check if str ends with searchStr
  // Optional length parameter
}

endsWith("hello", "lo");   // true
endsWith("hello", "ll");   // false
```

**4. Solve a common problem:**

Choose one:
- Reverse a string
- Check palindrome
- Count character frequency
- Find first non-repeated character
- Check anagram

**5. Implement replaceAll:**

```javascript
function replaceAll(str, searchStr, replaceStr) {
  // Replace ALL occurrences (not just the first)
}

replaceAll("hello hello", "hello", "hi");  // "hi hi"
replaceAll("aaa", "a", "b");                // "bbb"
```

* * *

## Common Mistakes

### Mistake 1: Forgetting String Immutability

```javascript
// WRONG - trying to modify a string
let str = "hello";
str[0] = "H";
console.log(str); // "hello" (unchanged)

// RIGHT - create a new string
function capitalize(str) {
  return str[0].toUpperCase() + str.slice(1);
}
```

### Mistake 2: Off-by-One Errors

```javascript
// WRONG - loop goes one too far
for (let i = 0; i <= str.length; i++) {
  // Accesses str[str.length] which is undefined
}

// RIGHT
for (let i = 0; i < str.length; i++) {
  // Safe access
}
```

### Mistake 3: Not Handling Empty Strings

```javascript
// WRONG - crashes on empty string
function firstChar(str) {
  return str[0].toUpperCase(); // Error if str is ""
}

// RIGHT
function firstChar(str) {
  if (str.length === 0) return "";
  return str[0].toUpperCase();
}
```

### Mistake 4: Ignoring Edge Cases

```javascript
// WRONG - doesn't handle negatives
function slice(str, start, end) {
  // What if start is negative?
}

// RIGHT
function slice(str, start = 0, end = str.length) {
  if (start < 0) start = Math.max(0, str.length + start);
  if (end < 0) end = Math.max(0, str.length + end);
  // ... continue
}
```

### Mistake 5: Inefficient Concatenation

```javascript
// SLOW - creates new string each iteration
function repeat(str, count) {
  let result = "";
  for (let i = 0; i < count; i++) {
    result += str; // Creates new string each time
  }
  return result;
}

// BETTER - still works, same approach is fine for interviews
// For production, consider array join method
function repeat(str, count) {
  let result = "";
  for (let i = 0; i < count; i++) {
    result += str;
  }
  return result;
}
```

* * *

## Quick Recap

- **String methods** are built-in functions that operate on strings.

- **Strings are sequences of characters** that you can access by index.

- **Strings are immutable** - methods return new strings, not modify the original.

- **A polyfill replicates** the functionality of a built-in method.

- **Why polyfills matter:**
  - Understand how built-ins work
  - Prepare for interviews
  - Provide fallbacks for older browsers

- **Core pattern for any polyfill:**
  1. Validate input
  2. Initialize result
  3. Loop and process
  4. Return result

- **Common interview problems:**
  - Reverse string
  - Palindrome check
  - Character frequency
  - Anagram detection
  - String compression
  - Substring without repeating chars

- **Edge cases matter** - empty strings, negative indices, out-of-bounds access.

- **Built-in behavior understanding** helps you:
  - Debug faster
  - Choose the right method
  - Optimize code
  - Pass interviews

Build your own string methods. Understand how they work. Master the fundamentals.

Happy coding! 🚀

* * *

> If you enjoyed this article, check out my other blogs on this profile. 🔗 Connect with me: [LinkedIn](https://linkedin.com/in/rajharsh03) | [GitHub](https://github.com/RajHarsh03) | [X (Twitter)](https://x.com/rajharsh03)
