Skip to main content

Command Palette

Search for a command to run...

String Polyfills and Common Interview Methods in JavaScript

Master string methods by building them from scratch

Updated
7 min read
String Polyfills and Common Interview Methods in JavaScript
A

Hi, I’m Abdul Samad. A web development learner and tech enthusiast. I write about what I learn, share practical coding tips, and publish in-depth blogs on programming and modern web development.

Check out my full collection of blogs on Hashnode: https://abdulsamad30.hashnode.dev/

Connect with me on X for quick updates and insights: @abdul_sama60108

1. What String Methods Actually Are

In JavaScript, a string isn't just text. It's a primitive value that gets temporarily wrapped in a String object whenever you call a method on it.

That's why you can write "hello".toUpperCase() on a raw string literal JavaScript does the object-wrapping behind the scenes.

String methods live on String.prototype.

When you call str.includes("world"), JavaScript climbs the prototype chain, finds the includes function defined there, and calls it with your string as this.

They don't mutate the original string. Strings in JavaScript are immutable. Every method returns a new value.

const name = "samad";

// toUpperCase lives on String.prototype
console.log(name.toUpperCase());

// original string is untouched
console.log(name);

Output:

SAMAD
samad

2. Why Developers Write Polyfills

A polyfill is a piece of code that implements a feature for environments where it doesn't exist natively. The word comes from "Polyfilla" a paste used to fill cracks in walls. You fill in the missing gaps in older environments.

JavaScript engines in older browsers don't always support the latest built-in methods. String.prototype.includes, for example, was added in ES2015. A browser from 2012 doesn't know what that is. A polyfill manually defines the method on the prototype if it's missing, so the rest of your code can use it as if it was always there.

Think of it like a universal adapter plug. Your laptop charger might not fit the outlets in a different country. The adapter doesn't change the charger it fills in the missing connection on the environment's end.

But polyfills aren't only about browser support. Writing one from scratch forces you to understand what the method is actually doing. That's why they show up in interviews. If you can implement includes yourself, it proves you understand character-by-character comparison, return types, and edge cases not just that you've memorized the API.

// Only add the method if it's not already defined
if (!String.prototype.customMethod) {
  String.prototype.customMethod = function() {
    // 'this' refers to the string instance
    return this;
  };
}

The if guard is important. You never want to overwrite a native implementation.


3. Implementing Simple String Utilities

Let's build four utilities from scratch. Each one mirrors a real built-in method. The goal isn't to reinvent the wheel it's to understand how the wheel rolls.

myIncludes does the string contain a substring?

The native includes method scans through the string looking for the first occurrence of a search term. At every position, it checks if the next N characters match the search string.

String.prototype.myIncludes = function(search) {
  const str = this;

  for (let i = 0; i <= str.length - search.length; i++) {
    if (str.substring(i, i + search.length) === search) {
      return true;
    }
  }

  return false;
};

console.log("javascript".myIncludes("script"));
console.log("javascript".myIncludes("python"));

Output:

true
false

myRepeat repeat a string N times

String.prototype.myRepeat = function(count) {
  if (count < 0) {
    throw new RangeError("count must be non-negative");
  }

  let result = "";

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

  return result;
};

console.log("ha".myRepeat(3));
console.log("-".myRepeat(5));

Output:

hahaha
-----

myTrim strip whitespace from both ends

Trim finds the first non-whitespace character from the left and the first from the right, then returns the slice between them.

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

  while (start <= end && str[start] === " ") {
    start++;
  }

  while (end >= start && str[end] === " ") {
    end--;
  }

  return str.slice(start, end + 1);
};

console.log("  hello world  ".myTrim());

Output:

"hello world"

myStartsWith does the string begin with a given prefix?

String.prototype.myStartsWith = function(prefix) {
  if (prefix.length > this.length) {
    return false;
  }

  for (let i = 0; i < prefix.length; i++) {
    if (this[i] !== prefix[i]) {
      return false;
    }
  }

  return true;
};

console.log("hello world".myStartsWith("hello"));
console.log("hello world".myStartsWith("world"));

Output:

true
false

4. Common Interview String Problems

Interview questions usually pick a string, give you a constraint, and ask you to solve it without touching a high-level method that does the work for you.

Reverse a string

The built-in approach is str.split("").reverse().join(""), but interviewers will often say "do it without those methods." The manual version uses two pointers walking toward each other.

function reverseString(str) {
  const chars = str.split("");
  let left = 0;
  let right = chars.length - 1;

  while (left < right) {
    [chars[left], chars[right]] = [chars[right], chars[left]];
    left++;
    right--;
  }

  return chars.join("");
}

console.log(reverseString("javascript"));

Output:

tpircsavaj

Check if a string is a palindrome

A palindrome reads the same forwards and backwards. You compare the character at index i from the start against the character at index i from the end.

function isPalindrome(str) {
  const cleaned = str.toLowerCase();

  for (let i = 0; i < Math.floor(cleaned.length / 2); i++) {
    if (cleaned[i] !== cleaned[cleaned.length - 1 - i]) {
      return false;
    }
  }

  return true;
}

console.log(isPalindrome("racecar"));
console.log(isPalindrome("hello"));

Output:

true
false

Count character occurrences

Given a string, return how many times each character appears. This pattern also underlies "find the first non-repeating character" a very common follow-up.

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

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

  return freq;
}

console.log(charFrequency("hello"));

Output:

{ h: 1, e: 1, l: 2, o: 1 }

Truncate a string with ellipsis

If a string is longer than a given limit, cut it and add "..." at the end.

function truncate(str, maxLength) {
  if (str.length <= maxLength) {
    return str;
  }

  return str.slice(0, maxLength - 3) + "...";
}

console.log(truncate("hello world this is JavaScript", 15));
console.log(truncate("short", 10));

Output:

hello world t...
short

Before writing any solution, ask what edge cases you should handle. Empty string? Negative count? Search term longer than the string? Handling these shows you think beyond the happy path.


5. Why Built-In Behavior Matters

There's a difference between knowing that slice(-1) gives you the last character and understanding why it does.

JavaScript's slice normalizes negative indices by adding them to the string's length. So "hello".slice(-1) is internally treated as "hello".slice(4). If you don't know that, you'll get confused the moment an interview question involves negative indices.

The same goes for indexOf vs includes. Both check if a substring exists, but indexOf returns a position while includes returns a boolean. Reaching for the right tool in the right situation is something you only develop by knowing what's going on under the hood.

const str = "hello";

// negative index — gets last character
console.log(str.slice(-1));

// indexOf returns position, -1 if not found
console.log(str.indexOf("l"));
console.log(str.indexOf("z"));

// includes returns boolean
console.log(str.includes("ell"));

Output:

o
2
-1
true

Writing polyfills is one of the best ways to build this understanding.

When you implement trim from scratch, you think about where the boundaries are and how to handle an all-whitespace input. Those are the same thoughts the spec authors had when designing the method.

Built-in methods are not magic. They're just functions that someone else wrote, optimized, and put on the prototype.

When you understand how they work, you can predict what they'll do in edge cases and that's exactly the skill that separates an average interview answer from a confident one.

FIN ✌️