String Polyfills and Common Interview Methods in JavaScript
Polyfills: Rebuilding JavaScript's Magic from Scratch ๐ง
Turning chai into code and ideas into full-stack applications. Sharing lessons from my development journey, one commit at a time.
The Broken Library Moment ๐
It was a cold Tuesday morning when I joined my first real dev job. The senior engineer handed me a task: "Make our app work on Internet Explorer 11."
I laughed. "IE11? Who uses that anymore?"
He didn't laugh back. "Our biggest client's entire company. 50,000 employees. They can't upgrade yet."
I ran the app in IE11. Red errors everywhere. The console screamed:
TypeError: 'includes' is undefined
TypeError: 'startsWith' is undefined
My modern JavaScript code was useless. All those beautiful ES6 string methods? They didn't exist in IE11.
That's when I learned about polyfills โ code that rebuilds modern JavaScript features from scratch using older JavaScript. It's like building a smartphone app using only a flip phone's parts.
By the end of that day, I'd written my first polyfill. And I finally understood how JavaScript actually works under the hood.
What Are String Methods? ๐ฏ
String methods are built-in functions that JavaScript provides to manipulate text. Think of them as power tools for strings.
const text = "Hello, World!";
// Built-in methods
text.toUpperCase(); // "HELLO, WORLD!"
text.includes("World"); // true
text.startsWith("Hello"); // true
text.slice(0, 5); // "Hello"
text.split(","); // ["Hello", " World!"]
The magic: You don't write the logic. JavaScript does it for you.
The problem: Not all browsers have all methods. Older browsers are missing the newer ones.
The solution: Polyfills โ we write the missing methods ourselves.
Why Do Developers Write Polyfills? ๐ก
The Browser Fragmentation Problem
Different browsers support different features at different times:
// This works in Chrome 41+ (2015)
"hello".includes("ell"); // true
// But breaks in IE11 (even in 2020!)
// TypeError: Object doesn't support property or method 'includes'
Three options when a method is missing:
Tell users to upgrade โ They won't. Especially corporate users.
Rewrite all your code โ Tedious and error-prone.
Write a polyfill โ Add the missing method yourself!
What Is a Polyfill?
A polyfill is code that implements a modern feature using older JavaScript.
The Pattern:
// Check if the method exists
if (!String.prototype.includes) {
// If not, add it ourselves
String.prototype.includes = function(search) {
// Our implementation here
};
}
Why this works:
If the browser already has
includes, we don't override itIf it's missing, we add our version
The rest of our code works the same way!
Understanding Built-in Behavior: How Methods Actually Work ๐
Before we write polyfills, we need to understand what the built-in methods actually do behind the scenes.
Example: How includes() Really Works
"hello".includes("ell"); // true
What JavaScript does internally:
Loop through the string character by character
At each position, check if the search string matches
Return
trueif found,falseif not
The actual logic:
function includes(searchString) {
// Loop through each character
for (let i = 0; i < this.length; i++) {
// Check if the substring matches at this position
let matches = true;
for (let j = 0; j < searchString.length; j++) {
if (this[i + j] !== searchString[j]) {
matches = false;
break;
}
}
if (matches) return true;
}
return false;
}
Building Our First Polyfill: String.prototype.includes() ๐ ๏ธ
Let's recreate includes() from scratch!
Step 1: The Basic Structure
if (!String.prototype.includes) {
String.prototype.includes = function(search, start) {
'use strict';
// Implementation goes here
};
}
Why add to String.prototype?
Makes it available on ALL strings
"hello".includes()works because strings inherit fromString.prototype
Step 2: Handle Edge Cases
if (!String.prototype.includes) {
String.prototype.includes = function(search, start) {
'use strict';
// Handle undefined/null
if (this == null) {
throw new TypeError('Cannot read property of null or undefined');
}
// Convert to string
const str = String(this);
// Default start position to 0
start = start || 0;
// Handle negative start (count from end)
if (start < 0) {
start = Math.max(0, str.length + start);
}
// Implementation continues...
};
}
Step 3: The Core Logic
if (!String.prototype.includes) {
String.prototype.includes = function(search, start) {
'use strict';
if (this == null) {
throw new TypeError('Cannot read property of null or undefined');
}
const str = String(this);
start = start || 0;
if (start < 0) {
start = Math.max(0, str.length + start);
}
// The actual search logic
return str.indexOf(search, start) !== -1;
};
}
Why use indexOf?
It's older and widely supported (IE6+)
Returns
-1if not found, otherwise returns positionWe just check if it's NOT
-1
Testing Our Polyfill
// Test cases
console.log("hello".includes("ell")); // true
console.log("hello".includes("bye")); // false
console.log("hello".includes("lo", 3)); // true (start at index 3)
console.log("hello".includes("he", 1)); // false (start at index 1)
// Edge cases
console.log("".includes("")); // true
console.log("hello".includes("", 2)); // true
All tests pass!
More String Polyfills: Building Your Toolkit ๐งฐ
Polyfill 2: String.prototype.startsWith()
if (!String.prototype.startsWith) {
String.prototype.startsWith = function(search, position) {
'use strict';
position = position || 0;
// Simple approach: slice the beginning and compare
return this.substr(position, search.length) === search;
};
}
// Usage
console.log("Hello World".startsWith("Hello")); // true
console.log("Hello World".startsWith("World", 6)); // true
How it works:
Extract the first N characters (where N = search length)
Compare directly with the search string
Easy and readable!
Polyfill 3: String.prototype.endsWith()
if (!String.prototype.endsWith) {
String.prototype.endsWith = function(search, length) {
'use strict';
if (length === undefined || length > this.length) {
length = this.length;
}
// Slice from the end
return this.substring(length - search.length, length) === search;
};
}
// Usage
console.log("Hello World".endsWith("World")); // true
console.log("Hello World".endsWith("Hello")); // false
console.log("Hello World".endsWith("Hello", 5)); // true (only check first 5 chars)
Polyfill 4: String.prototype.repeat()
if (!String.prototype.repeat) {
String.prototype.repeat = function(count) {
'use strict';
if (this == null) {
throw new TypeError('Cannot read property of null or undefined');
}
count = Math.floor(count);
if (count < 0 || count === Infinity) {
throw new RangeError('Invalid count value');
}
let result = '';
for (let i = 0; i < count; i++) {
result += this;
}
return result;
};
}
// Usage
console.log("Ha".repeat(3)); // "HaHaHa"
console.log("*".repeat(5)); // "*****"
console.log("Echo".repeat(0)); // ""
Interview Optimization:
// Better approach: O(log n) using doubling
String.prototype.repeat = function(count) {
'use strict';
let str = this.toString();
count = Math.floor(count);
let result = '';
while (count > 0) {
if (count & 1) { // If count is odd
result += str;
}
str += str; // Double the string
count >>= 1; // Divide count by 2
}
return result;
};
Polyfill 5: String.prototype.trim()
if (!String.prototype.trim) {
String.prototype.trim = function() {
'use strict';
// Use regex to remove whitespace from both ends
return this.replace(/^\s+|\s+$/g, '');
};
}
// Usage
console.log(" hello ".trim()); // "hello"
console.log("\n\tworld\n".trim()); // "world"
console.log(" spaces ".trim()); // "spaces"
Regex breakdown:
^\s+โ Match whitespace at the start (^)|โ OR\s+\(โ Match whitespace at the end (\))gโ Global flag (replace all matches)
Common Interview String Problems ๐ฏ
Polyfills are interview favorites. Here are the patterns you'll see:
Interview Question 1: Implement String.prototype.padStart()
// Built-in behavior
"5".padStart(3, "0"); // "005"
"hello".padStart(10, "*"); // "*****hello"
Your Polyfill:
if (!String.prototype.padStart) {
String.prototype.padStart = function(targetLength, padString) {
'use strict';
targetLength = Math.floor(targetLength);
padString = String(padString || ' ');
if (this.length >= targetLength) {
return String(this);
}
const padLength = targetLength - this.length;
// Repeat padString enough times
let padding = padString.repeat(Math.ceil(padLength / padString.length));
// Trim to exact length needed
return padding.slice(0, padLength) + this;
};
}
// Test
console.log("5".padStart(3, "0")); // "005"
console.log("hello".padStart(10, "*")); // "*****hello"
console.log("test".padStart(10)); // " test"
Interview Question 2: Implement String.prototype.padEnd()
if (!String.prototype.padEnd) {
String.prototype.padEnd = function(targetLength, padString) {
'use strict';
targetLength = Math.floor(targetLength);
padString = String(padString || ' ');
if (this.length >= targetLength) {
return String(this);
}
const padLength = targetLength - this.length;
let padding = padString.repeat(Math.ceil(padLength / padString.length));
// Add padding to the END instead
return this + padding.slice(0, padLength);
};
}
// Usage
console.log("5".padEnd(3, "0")); // "500"
console.log("hello".padEnd(10, ".")); // "hello....."
Interview Question 3: Implement String.prototype.replaceAll()
if (!String.prototype.replaceAll) {
String.prototype.replaceAll = function(search, replace) {
'use strict';
// If search is a regex with 'g' flag
if (search instanceof RegExp) {
if (!search.global) {
throw new TypeError('replaceAll must be called with a global RegExp');
}
return this.replace(search, replace);
}
// For strings, escape special regex chars and add 'g' flag
const escapedSearch = search.replace(/[.*+?^\({}()|[\]\\]/g, '\\\)&');
return this.replace(new RegExp(escapedSearch, 'g'), replace);
};
}
// Usage
const text = "hello world, hello everyone";
console.log(text.replaceAll("hello", "hi"));
// "hi world, hi everyone"
console.log("aaa".replaceAll("a", "b"));
// "bbb"
Why Understanding Built-in Behavior Matters ๐ง
Reason 1: Interview Success
Interviewers LOVE asking:
"Implement
mapwithout usingmap""Write your own
filter""Create a
debouncefunction"
These are all polyfill questions in disguise. If you understand how built-ins work, you can rebuild them.
Reason 2: Debugging Skills
When something breaks, understanding the internals helps you debug faster.
// Bug report: "includes() returns wrong result"
const text = "Hello";
console.log(text.includes("HELLO")); // false โ WHY?
// If you wrote the polyfill, you'd know:
// includes() is CASE-SENSITIVE by design!
// Solution: convert to same case first
console.log(text.toLowerCase().includes("hello")); // true โ
Reason 3: Performance Optimization
Sometimes the built-in isn't the fastest. Understanding how it works lets you optimize.
// Slow: repeated includes() in a loop
for (let i = 0; i < bigArray.length; i++) {
if (bigArray[i].includes(searchTerm)) {
// process
}
}
// Faster: use indexOf once, save the result
const index = bigString.indexOf(searchTerm);
if (index !== -1) {
// process
}
Reason 4: Confidence in Your Code
When you know exactly what split() does internally, you use it more confidently. No guessing. No trial-and-error.
// Confident usage because you understand the logic
const parts = "apple,banana,cherry".split(",");
// You KNOW this returns ["apple", "banana", "cherry"]
// Without understanding:
const parts = "apple,banana,cherry".split(",");
// "Hmm, does this work? Let me console.log() to check..."
Interview Tips: Acing Polyfill Questions ๐ผ
Tip 1: Always Check If It Exists First
// CORRECT
if (!String.prototype.myMethod) {
String.prototype.myMethod = function() { /* ... */ };
}
// WRONG: Overwrites the built-in!
String.prototype.myMethod = function() { /* ... */ };
Interviewer will ask: "What if the browser already has this method?"
Your answer: "We check first and only add if it's missing. Never override built-ins."
Tip 2: Handle Edge Cases
// Check for null/undefined
if (this == null) {
throw new TypeError('...');
}
// Convert to correct type
const str = String(this);
// Handle negative indices
if (start < 0) {
start = Math.max(0, str.length + start);
}
Interviewer will test: null.includes(), negative positions, empty strings.
Your code: Already handles it!
Tip 3: Explain Your Approach
// Silence while coding
function includes(search) {
return this.indexOf(search) !== -1;
}
// Talk through it
"So includes() needs to search for a substring.
The simplest approach is to use indexOf(), which returns -1 if not found.
We just check if the result is NOT -1.
This works because indexOf() is older and widely supported."
Tip 4: Know Time Complexity
Interviewer: "What's the time complexity?"
You:
includes(): O(n ร m) where n = string length, m = search lengthrepeat()naive: O(n) where n = countrepeat()optimized: O(log n)trim(): O(n) with regex
Practice Challenges ๐๏ธ
Challenge 1: Implement String.prototype.capitalize()
Make the first letter uppercase, rest lowercase.
"hello".capitalize(); // "Hello"
"WORLD".capitalize(); // "World"
"jAvAsCrIpT".capitalize(); // "Javascript"
Solution:
String.prototype.capitalize = function() {
'use strict';
if (!this || this.length === 0) {
return this;
}
return this.charAt(0).toUpperCase() + this.slice(1).toLowerCase();
};
Challenge 2: Implement String.prototype.truncate(maxLength, suffix)
Shorten a string and add "..." if too long.
"Hello World".truncate(8); // "Hello..."
"Hello World".truncate(8, "โฆ"); // "Hello Wโฆ"
"Hi".truncate(10); // "Hi"
Solution:
String.prototype.truncate = function(maxLength, suffix = '...') {
'use strict';
if (this.length <= maxLength) {
return this.toString();
}
return this.slice(0, maxLength - suffix.length) + suffix;
};
Challenge 3: Implement String.prototype.reverse()
Reverse the string.
"hello".reverse(); // "olleh"
"12345".reverse(); // "54321"
Solution:
String.prototype.reverse = function() {
'use strict';
return this.split('').reverse().join('');
};
The IE11 Ending ๐ฌ
Remember that cold Tuesday morning? After writing polyfills for includes, startsWith, repeat, and a dozen more, I ran the app in IE11 again.
Green console. No errors.
My senior engineer looked over my shoulder. "Nice work. How do you feel?"
I smiled. "I feel like I actually understand JavaScript now. I always used .includes() without knowing what it was doing. Now I know exactly how it works because I built it myself."
He nodded. "That's the point. Polyfills aren't just about browser support. They're about understanding. When you can rebuild something from scratch, you truly know it."
Three years later, I still write polyfillsโnot because I need IE11 support, but because it's the best way to learn how JavaScript works under the hood.
Key Takeaways ๐ฏ
Polyfills bridge the gap between old browsers and new features
Understanding built-ins makes you a better developer
Interviews love polyfill questions โ master them!
Check before adding: Never override existing methods
Handle edge cases: null, undefined, negative indices
Talk through your logic: Explain your approach
Practice, practice, practice: Build these from scratch
Your Turn! ๐
Pick a string method you use daily and try rebuilding it from scratch. You'll be amazed at how much you learn.
Next steps:
Try implementing
Array.prototype.map()as a polyfillChallenge: Build
Array.prototype.filter()without using loops (use recursion!)Advanced: Implement
Promise.prototype.finally()