Mastering JavaScript Strings: From Built-in Magic to Custom Polyfills
Strings are one of the most commonly used data types in JavaScript. Whether you're building a website, processing user input, or solving coding problems, you’ll use string methods almost everywhere.
What Are String Methods?
Imagine a string as a row of labeled boxes — each box holds one character. JavaScript gives you a powerful toolkit to work with these boxes: string methods are built-in functions that let you search, slice, reshape, and transform text without writing everything from scratch.
💡Simple analogy
Think of a string like a word on a neon sign. String methods are like tools — scissors to cut it, a highlighter to find letters, a stamp to replace words.
When you write "hello".toUpperCase(), JavaScript internally loops through each character and returns its uppercase version. You didn't write that loop — the browser did. Understanding what's happening behind the scenes is exactly what interviews test.
Most-used string methods at a glance
The
toUpperCase()andtoLowerCase()methods convert all characters in a string to uppercase or lowercase. They always return a new string and do not modify the original string.The
trim(),trimStart(), andtrimEnd()methods remove whitespace from a string.trim()removes spaces from both ends, while the other two remove spaces only from the start or the end.The
includes(searchStr)method checks whether a string contains a specific substring. It returnstrueif the substring is found, otherwise it returnsfalse.The
startsWith()andendsWith()methods check whether a string begins or ends with a specific sequence of characters.The
slice(start, end)method extracts a portion of a string starting from the givenstartindex up to, but not including, theendindex.The
repeat(n)method returns a new string where the original string is repeatedntimes. If a negative number is provided, it throws an error.The
padStart()andpadEnd()methods add characters (or spaces) to the beginning or end of a string until it reaches a specified length.The
replace()andreplaceAll()methods are used to replace parts of a string.replace()changes only the first occurrence, whilereplaceAll()changes all occurrences of the specified pattern.
Why Do Developers Write Polyfills?
A polyfill is a piece of code that adds a missing feature to older environments. For example, String.prototype.includes was introduced in ES6 (2015). An older browser that predates ES6 doesn't know what includes is — so a polyfill teaches it.
🌍Real-world scenario
A company's internal tool still runs on an ancient browser version for compliance reasons. You can't use
.includes()there — you write a polyfill to fill the gap, and your code works everywhere.
Beyond compatibility, interviewers love polyfill questions because writing one proves you understand exactly how a method works — its edge cases, return values, and behavior on unusual input. You can't fake that knowledge.
String Processing Flow
Before writing a polyfill, it helps to visualize how a string method processes your input. Every method follows a predictable pattern regardless of what it does specifically.
Notice that strings in JavaScript are immutable — every method returns a new string rather than modifying the original. This is a fact interviewers love to probe. Always keep it in mind when writing your polyfill implementations.
Implementing String Polyfills
Let's build our own versions of common methods. The goal isn't to replace JavaScript's built-ins — it's to understand their logic so deeply that you could rebuild them from scratch.
⚠️Pattern to follow every time
First check if the method already exists (don't override native implementations), then define it on
String.prototypeso all strings get access to it automatically.
1. myTrim() — Remove leading & trailing spaces
The built-in .trim() removes whitespace from both ends. Here's the logic step by step: find where real characters start, find where they end, then slice between those two positions.
// Only add our polyfill if the native one doesn't exist
if (!String.prototype.myTrim) {
String.prototype.myTrim = function() {
let start = 0;
let end = this.length - 1;
// Move start forward past whitespace
while (start <= end && this[start] === ' ') {
start++;
}
// Move end backward past whitespace
while (end >= start && this[end] === ' ') {
end--;
}
// Slice out just the middle part
return this.slice(start, end + 1);
};
}
// Usage
console.log(" hello world ".myTrim()); // "hello world"
2. myIncludes() — Check if a substring exists
The strategy: slide a window of the same length as the search string across the original string and check for a match at each position.
String.prototype.myIncludes = function(searchStr) {
// Guard: if nothing to search for, it's always true
if (searchStr === '') return true;
const len = this.length;
const searchLen = searchStr.length;
for (let i = 0; i <= len - searchLen; i++) {
// Check if a window starting at i matches searchStr
if (this.slice(i, i + searchLen) === searchStr) {
return true;
}
}
return false;
};
console.log("javascript".myIncludes("script")); // true
console.log("javascript".myIncludes("python")); // false
3. myRepeat() — Repeat a string n times
Build the result by concatenating the string in a loop. Pay attention to the edge cases — negative numbers and Infinity must throw errors, just like the native method does.
String.prototype.myRepeat = function(count) {
// Match native behavior: throw on invalid input
if (count < 0 || count === Infinity) {
throw new RangeError("Invalid count value");
}
let result = '';
for (let i = 0; i < Math.floor(count); i++) {
result += this; // 'this' is the string being repeated
}
return result;
};
console.log("ha".myRepeat(3)); // "hahaha"
console.log("go".myRepeat(0)); // "" (empty string)
4. myStartsWith() — Check the beginning of a string
The key insight: just compare the first n characters of the string to the search string, where n equals the search string's length. Optionally, a starting position can be passed.
String.prototype.myStartsWith = function(searchStr, position = 0) {
// Clamp position to a valid range
const start = Math.max(0, Math.min(position, this.length));
// Grab a slice of the same length as searchStr
return this.slice(start, start + searchStr.length) === searchStr;
};
console.log("interview".myStartsWith("inter")); // true
console.log("interview".myStartsWith("view", 5)); // true
Common Interview String Problems
Here are the interview problems that come up most frequently — ranked by how often they appear. Each solution uses only the logic you'd build into a polyfill.
Q1. Reverse a string without using .reverse()
Strings in JavaScript don't have a built-in .reverse() like arrays do. The trick is to read the string backwards using a simple loop.
function reverseString(str) {
let reversed = '';
for (let i = str.length - 1; i >= 0; i--) {
reversed += str[i]; // Append each char from the end
}
return reversed;
}
console.log(reverseString("hello")); // "olleh"
Q2. Check if a string is a palindrome
A palindrome reads the same forward and backward (e.g., "racecar"). Compare the original string with its reversed version — or compare characters from both ends moving inward.
function isPalindrome(str) {
const clean = str.toLowerCase().replace(/[^a-z0-9]/g, '');
let left = 0;
let right = clean.length - 1;
while (left < right) {
if (clean[left] !== clean[right]) return false;
left++;
right--;
}
return true;
}
console.log(isPalindrome("racecar")); // true
console.log(isPalindrome("A man, a plan")); // false
Q3. Count the frequency of each character
Build a frequency map by iterating through the string and tracking how many times each character has appeared using an object.
function charFrequency(str) {
const freq = {};
for (const char of str) {
freq[char] = (freq[char] || 0) + 1;
}
return freq;
}
console.log(charFrequency("hello"));
// { h: 1, e: 1, l: 2, o: 1 }
Q4. Capitalize the first letter of each word (Title Case)
Split the string into words, capitalize the first character of each, then join them back. This is a clean combination of several string methods working together.
function titleCase(str) {
return str
.split(' ')
.map(word =>
word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()
)
.join(' ');
}
console.log(titleCase("the quick brown fox"));
// "The Quick Brown Fox"
Q5. Find the first non-repeating character
Build a frequency map first, then walk through the string and return the first character whose count is exactly 1. If none exists, return null.
function firstUnique(str) {
const freq = {};
// First pass: count all characters
for (const char of str) {
freq[char] = (freq[char] || 0) + 1;
}
// Second pass: find first with count of 1
for (const char of str) {
if (freq[char] === 1) return char;
}
return null; // All characters repeat
}
console.log(firstUnique("aabbcde")); // "c"
console.log(firstUnique("aabb")); // null
Quick Reference — Native vs Polyfill at a Glance
| Method | What it does | Core logic in polyfill | Edge case to remember |
|---|---|---|---|
| .trim() | Remove surrounding spaces | Two-pointer walk from both ends | Tabs & newlines are also whitespace |
| .includes() | Check for substring | Sliding window comparison | Empty string always returns true |
| .repeat() | Repeat string n times | Loop and concatenate | Negative or Infinity → RangeError |
| .startsWith() | Check string beginning | Slice and compare | Accepts an optional start position |
| .padStart() | Pad to target length | Prepend pad chars in a loop | Pad string can be multi-char |
Key Takeaways
Before you close this tab, lock these ideas in. They're the core of what interviewers are really testing when they ask string questions.
🎓The underlying principle
Interviewers don't care if you memorize method names. They care whether you understand the logic well enough to rebuild a method from scratch and handle its edge cases correctly.
Strings are immutable in JavaScript. Every string method returns a brand-new string — the original is never modified. Always return a new value in your polyfills.
Check if the method already exists before adding your polyfill. Use
if (!String.prototype.myMethod)to avoid overriding native implementations accidentally.Edge cases matter more than happy paths. Empty strings,
null, negative counts, overlapping patterns — these are exactly what interviewers probe.Match native behavior exactly. If the native method throws a
RangeError, your polyfill should too. Read the spec or MDN before writing a polyfill.Frequency maps are your best friend. A huge number of string problems — first unique character, anagram detection, most frequent character — all boil down to building and querying a frequency map.
Two-pointer technique is everywhere. Palindrome checking, trim logic, and anagram detection all use two pointers walking from opposite ends of the string toward the middle.
Next steps for practiceTry writing polyfills for
.endsWith(),.padEnd(), and.replaceAll()on your own. Then solve "check if two strings are anagrams" and "find the longest palindromic substring" — two of the most common interview problems that build directly on everything you've learned here.