Skip to main content

Command Palette

Search for a command to run...

String Polyfills and Common String Methods in JavaScript

Published
7 min read
String Polyfills and Common String Methods in JavaScript

Here's a question that shows up in JavaScript interviews more often than you'd expect:

Can you implement your own version of String.prototype.includes()?

And here's what usually happens. The candidate has used includes() hundreds of times. They know exactly what it does. But when asked to write it from scratch, without calling the built-in, they stuck. Not because they are not smart. But because they've been using the method without ever thinking about the logic inside it.

This article is about closing that gap.

String Polyfills and Common Interview Methods in JavaScript are essential topics for anyone preparing for coding interviews or trying to deepen their understanding of JavaScript fundamentals.

Why This Topic is Important for Interviews

Interviewers often test:

  • Your understanding of core JavaScript concepts

  • Your ability to think logically

  • Your skill in writing clean, efficient code

1. What Are String Methods

In simple terms, string methods are built-in tools that help you manipulate text.

In JavaScript, strings are primitive values... but they behave like objects when you access properties on them. When you write "hello".toUpperCase(), JavaScript temporarily wraps the string in a String object, finds the toUpperCase method on String.prototype, calls it, and returns the result.

String.prototype is essentially a shared shelf of methods that every string in JavaScript has access to. When you call a method on a string, JavaScript goes to that shelf and borrow the method.

JavaScript provides many built-in methods such as : split() , trim() , replace() , includes() , toUpperCase() , toLowerCase() etc.

These methods simplify string manipulation.

Each of these methods takes the string, does something to it, and returns a result. They never modify the original string. Strings in JavaScript are immutable. Every string method returns a new value. The original stays untouched.

Example :

let name = "rohit";

// All of these live on String.prototype
name.toUpperCase();    // "ROHIT"
name.includes("hit");  // true
name.trim();           // "rohit" (no spaces to trim here)
name.startsWith("roh"); // true
name.repeat(2);        // "rohitrohit"

// After performing all these operation, name would have the same value as initial value
console.log(name)        //rohit (Immutable: no change in original string)

2. Why Developers Write Polyfills

The word polyfill sounds technical, but the idea is simple. A polyfill is custom code that replicates the behavior of a built-in method.

Imagine you're using a feature in a newer version of JavaScript.... let's say, String.prototype.padStart(), introduced in ES2017. Your code runs beautifully in Chrome. But then you test it in an older browser that doesn't know what padStart is, and it throws an error.

A polyfill is your solution now: write your own version of the missing method, and attach it to the prototype so it behaves exactly like the real thing would.

Code Implementation :

// Check if the method doesn't exist yet
if (!String.prototype.padStart) {
  // If not, add our own version
  String.prototype.padStart = function(targetLength, padString) {
    // we can write our implementation here
  };
}

The pattern is always the same:

  1. Check if the built-in already exists

  2. If it doesn't, define your own version with identical behavior

  3. Attach it to the prototype so every string can use it

One important rule: in production code, always check before adding to native prototypes. Adding to String.prototype without checking can break other code or conflict with future language updates.

3. Implementing Simple String Utilities

Now let's actually build some polyfills. For each one, we'll understand what the built-in does conceptually and then write our own version from scratch.

trim() — Remove Whitespace From Both Ends

What it does: Removes all leading and trailing whitespace from a string. Whitespace includes spaces, tabs, and newlines. The middle of the string is untouched.

"  hello world  ".trim()   // "hello world"
"   hi   ".trim()          // "hi"
"no spaces".trim()         // "no spaces"

The Logic : Walk from the left end to right until you hit a non-space character. Walk from the right end to left until you hit a non-space character. Return everything in between.

Implementation :

String.prototype.myTrim = function() {
  let str = this;
  let start = 0;
  let end = str.length - 1;

  // Move start forward while there's whitespace
  while (start <= end && str[start] === ' ') {
    start++;
  }

  // Move end backward while there's whitespace
  while (end >= start && str[end] === ' ') {
    end--;
  }

  // Return the slice between the two pointers
  return str.slice(start, end + 1);
};

// Uses
"  hello  ".myTrim();  // "hello"
"  hi there  ".myTrim(); // "hi there"

repeat() — Repeat a String N Times

What it does: Returns a new string made by repeating the original string a given number of times.

"ha".repeat(3)     // "hahaha"
"ab".repeat(4)     // "abababab"
"*".repeat(5)      // "*****"
"hi".repeat(0)     // ""

The logic: Start with an empty result. Add the string to it n times using a loop.

Implementation :

String.prototype.myRepeat = function(count) {
  if (count < 0) throw new RangeError("Invalid count value");
  if (count === 0) return "";

  let str = this.valueOf();
  let result = "";

  for (let i = 0; i < count; i++) {
    result += str;
  }

  return result;
};


//Uses
"ha".myRepeat(3);   // "hahaha"
"*".myRepeat(5);    // "*****"

4. Common Interview String Problems

Now let's move from polyfills to classic string problems. These are the kinds of questions that appear in real interviews, just to understand how you think through a problem.

Problem 1 : Reverse a String

// Using built-ins (what they might ask you NOT to use)
"hello".split("").reverse().join("")  // "olleh"


/*-------------------------------------------------------------------*/

// Manual approach — what they usually want to see
function reverseString(str) {
  let reversed = "";

  for (let i = str.length - 1; i >= 0; i--) {
    reversed += str[i];
  }

  return reversed;
}



reverseString("hello");       // "olleh"
reverseString("JavaScript");  // "tpircSavaJ"

Problem 2 : Check if a String is a Palindrome

function isPalindrome(str) {
  let cleaned = str.toLowerCase();
  let left = 0;
  let right = cleaned.length - 1;

  while (left < right) {
    if (cleaned[left] !== cleaned[right]) {
      return false;  // mismatch found — not a palindrome
    }
    left++;
    right--;
  }

  return true;
}



isPalindrome("madam");    // true
isPalindrome("racecar");  // true
isPalindrome("hello");    // false
isPalindrome("Level");    // true — case insensitive

Problem 3 — Count Occurrences of a Character

function countOccurrences(str, char) {
  let count = 0;

  for (let i = 0; i < str.length; i++) {
    if (str[i] === char) {
      count++;
    }
  }

  return count;
}

countOccurrences("javascript", "a");   // 2
countOccurrences("mississippi", "s");  // 4
countOccurrences("hello", "z");        // 0

Follow-up question : "What if we want to count all characters and return a frequency map?"

function charFrequency(str) {
  let freq = {};

  for (let char of str) {
    freq[char] = (freq[char] || 0) + 1;
  }

  return freq;
}


charFrequency("javascript");
// { j: 1, a: 2, v: 1, s: 1, c: 1, r: 1, i: 1, p: 1, t: 1 }

5. Importance of understanding built-in behavior

Every built-in string method has a specification. The JavaScript language defines exactly what trim() should do with unicode whitespace, what includes() should do with an empty search string, what padStart() should do if the pad string is longer than the padding needed. These edge cases are all defined.

// Edge cases you discover when implementing

"".myIncludes("");          // should return true — empty string is inside everything
"hello".myIncludes("");     // should return true
"hi".myRepeat(0);           // should return "" — zero repetitions
"hi".myRepeat(-1);          // should throw RangeError
"hello".myPadStart(3, "*"); // should return "hello" — already longer than target

Every one of these is a real behavior of the built-in method. When you implement the polyfill and discover these cases, you understand the built-in better than you did before. That understanding pays off when:

  • You're debugging unexpected output and need to know exactly what a method does in an edge case

  • You're doing a code review and spotting where a method might behave unexpectedly

  • You're in an interview and can speak to edge cases confidently

  • You're working on a performance-sensitive piece of code and know whether a built-in is the right tool