Skip to main content

Command Palette

Search for a command to run...

String Polyfills and Common Interview Methods in JavaScript

Updated
7 min readView as Markdown
String Polyfills and Common Interview Methods in JavaScript

So at some point in your JavaScript journey, someone's going to ask you in an interview " can you implement indexOf without using it ? " or "write your own trim function."

And if you've only ever used these methods without thinking about what they're actually doing underneath, that question hits different.

That's what this post is about understanding the logic behind string methods well enough to rebuild them yourself which also happens to be exactly what interviews test.

What String Methods Actually Are

String methods are built-in functions that JavaScript gives you on every string. Things like .toUpperCase(), .includes(), .trim(), .slice() they live on String.prototype, which means every string you create automatically has access to them.

const name = "ankur"
console.log(name.toUpperCase()) // ANKUR

You didn't write toUpperCase JavaScript shipped it. Most developers use these methods every day without ever thinking about what's happening inside them.

That's fine for production code. But for interviews and for actually understanding the language you want to know the logic.

What is a Polyfill ?

A polyfill is code you write to replicate something built-in usually because either the built-in doesn't exist in older environments, or you want to understand how it works from scratch. The word itself comes from "Polyfilla" a wall-filling product. You're filling in a gap.

In interview context, polyfills are really just logic exercises. "Implement this without using the native version." The point isn't that the native version is bad it's to test whether you understand what's happening under the hood.

Implementing Common String Utilities

Your Own includes

The built-in includes checks if a substring exists inside a string. How would you do that without it ?

function myIncludes(str, target) {
  for (let i = 0; i <= str.length - target.length; i++) {
    if (str.slice(i, i + target.length) === target) {
      return true
    }
  }
  return false
}

console.log(myIncludes("javascript", "script")) // true
console.log(myIncludes("javascript", "python")) // false

Slide a window of target.length across the string. At each position, check if that chunk matches the target. If it does at any point found it. If you reach the end without a match not there.

Your Own indexOf

Similar to includes but instead of returning true/false, return the position where the match starts. Return -1 if not found same as the built-in.

function myIndexOf(str, target) {
  for (let i = 0; i <= str.length - target.length; i++) {
    if (str.slice(i, i + target.length) === target) {
      return i;
    }
  }
  return -1
}

console.log(myIndexOf("javascript", "script")) // 4
console.log(myIndexOf("javascript", "python")) // -1

Same sliding window. Just return the index instead of true.

Your Own trim

trim removes whitespace from both ends of a string. Not from the middle just the edges.

function myTrim(str) {
  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(myTrim("  hello world  ")) // "hello world"
console.log(myTrim("   ankur"))        // "ankur"

Two pointers one from the left, one from the right. Move the left pointer forward while you're hitting spaces. Move the right pointer backward while you're hitting spaces. Slice what's in between.

Your Own repeat

function myRepeat(str, times) {
  if (times <= 0) return ""

  let result = ""
  for (let i = 0; i < times; i++) {
    result += str
  }
  return result
}

console.log(myRepeat("ha", 3)) // hahaha
console.log(myRepeat("ab", 0)) // ""

Simple loop. Build up the result string by appending str once per iteration.

Your Own startsWith

function myStartsWith(str, target) {
  return str.slice(0, target.length) === target
}

console.log(myStartsWith("javascript", "java")) // true
console.log(myStartsWith("javascript", "node")) // false

Cut the front of the string to the same length as the target. If it matches starts with it.

Your Own endsWith

function myEndsWith(str, target) {
  return str.slice(str.length - target.length) === target
}

console.log(myEndsWith("javascript", "script")) // true
console.log(myEndsWith("javascript", "java"))   // false

Same idea but from the other end. Slice the tail of the string and compare.

Common Interview String Problems

Beyond polyfills, these are the string problems that come up constantly in interviews.

Reverse a String

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

console.log(reverseString("ankur")) // rukna

Loop from the end to the start, build a new string. The one-liner version is str.split("").reverse().join("") but if they say "without built-ins", use the loop.

Check if a String is a Palindrome

A palindrome reads the same forwards and backwards. "racecar", "madam", "level".

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

  while (left < right) {
    if (str[left] !== str[right]) return false
    left++
    right--
  }
  return true
}

console.log(isPalindrome("racecar")) // true
console.log(isPalindrome("ankur"))   // false

Two pointers from both ends moving toward the middle. If they ever don't match not a palindrome. If they meet in the middle without a mismatch it is.

Count Vowels in a String

function countVowels(str) {
  const vowels = "aeiouAEIOU"
  let count = 0

  for (let char of str) {
    if (vowels.includes(char)) count++
  }
  return count
}

console.log(countVowels("javascript")) // 3
console.log(countVowels("Ankur"))      // 2

Loop through each character. Check if it's in your vowel string. Increment counter if yes.

Find the Most Repeated Character

function mostRepeated(str) {
  const freq = {}

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

  let maxChar = ""
  let maxCount = 0

  for (let char in freq) {
    if (freq[char] > maxCount) {
      maxCount = freq[char]
      maxChar = char
    }
  }

  return maxChar
}

console.log(mostRepeated("javascript")) // a

Build a frequency map first count how many times each character appears. Then scan the map for the highest count.

Check if Two Strings are Anagrams

Two words are anagrams if they have the exact same characters in a different order. "listen" and "silent" for example.

function isAnagram(str1, str2) {
  if (str1.length !== str2.length) return false

  const freq = {}

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

  for (let char of str2) {
    if (!freq[char]) return false
    freq[char]--
  }

  return true
}

console.log(isAnagram("listen", "silent")) // true
console.log(isAnagram("hello", "world"))   // false

Different lengths can't be anagrams check that first. Then count character frequencies in the first string. Go through the second string, decrement for each match. If a character isn't in the map or runs out not an anagram.

Why This Actually Matters

Knowing that .trim() exists and using it in production code is completely fine. That's the right thing to do. Built-in methods are optimized, tested, and maintained.

But understanding how trim works the two-pointer approach, moving from edges inward tells you something about the underlying logic. And that understanding transfers. The same two-pointer technique shows up in palindrome checks, in array problems, in a dozen other places. It's a pattern, not just an answer.

Interviews aren't really testing whether you've memorized polyfill implementations. They're testing whether you can reason through a problem without reaching for a shortcut. Can you break it down? Can you think about edge cases? Can you explain your approach? That only comes from understanding what the built-ins are doing, not just that they exist.