Understanding Polyfills in JavaScript (with a Custom bind() Example)

When working with JavaScript, especially in a browser environment, you’ll often encounter situations where a particular method or feature isn’t supported in all browsers. This is where polyfills come into play.

What is a Polyfill?

A polyfill is a piece of code that acts as a fallback for features that are not natively supported by certain browsers or JavaScript environments. It "fills in" the gap by implementing the missing functionality, ensuring consistent behavior across environments.


A Quick Recap of bind()

The bind() function creates a new function with the this keyword permanently set to the object you provide. It also lets you pass some arguments in advance.

Let's Start With an Example

let name = {
  firstName: 'Jack',
  lastName: 'Smith',
};

let printFullName = function(hometown, state, country) {
  console.log(this.firstName + ' ' + this.lastName, 'from', hometown, state, country);
};

let printMyName = printFullName.bind(name, 'Hyderabad', 'Telangana');
printMyName('India'); // Output: Jack Smith from Hyderabad Telangana India

What’s happening here?

  • bind() creates a new function printMyName.

  • It locks this to the name object.

  • It also pre-fills two arguments: 'Hyderabad' and 'Telangana'.

  • Later, we pass 'India' when calling the function.

Want to dive deeper into this, call(), apply(), and bind() in JavaScript?
Check out my detailed blog:
👉 Understanding this, bind(), call(), and apply() in JavaScript


Now, Let’s Build Our Own bind() – A Polyfill

To understand bind() more deeply, let's implement our own version called myBind() and handle various scenarios step by step.

Example:

let name = {
  firstName: 'Jack',
  lastName: 'Smith',
};

let printFullName = function() {
  console.log(this.firstName + ' ' + this.lastName);
};

// custom bind function
// if we keep any method in the Function prototype, we can use it on any function
Function.prototype.myBind = function (...args) {
  // Here args are used to get arguments passed to the myBind function

  // inside the function, 'this' refers to the function we are calling myBind on.
  let func = this; // In this example, 'this' refers to printFullName
  let obj = args[0] // First argument is the object we want to point to

  // bind should return a new function
  return function () {
    func.apply(obj);
  };
};

let printMyName = printFullName.myBind(name);
printMyName(); // Output: Jack Smith

⚠️ Note: In production code, it's not recommended to modify Function.prototype directly.
To create a polyfill safely, always check if the method already exists before defining it:

if (!Function.prototype.myBind) {
  Function.prototype.myBind = function (...) {
    // your polyfill logic here
  };
}

So far, so good! This handles the basic functionality of setting the this context.

What If We Want to Pass Arguments?

let name = {
  firstName: 'Jack',
  lastName: 'Smith',
};

let printFullName = function(hometown, state) {
  console.log(this.firstName + ' ' + this.lastName, 'from', hometown, state);
};

// custom bind function
Function.prototype.myBind = function (...args) {
  let func = this;
  let obj = args[0]

  return function () {
    func.apply(obj);
  };
};

let printMyName = printFullName.myBind(name, 'Hyderabad', 'Telangana');
printMyName(); // Output: Jack Smith from undefined undefined

We passed arguments, but it didn’t show up in the output. That’s because our polyfill doesn’t forward the arguments yet.

Fixing It: Forward the Arguments

let name = {
  firstName: 'Jack',
  lastName: 'Smith',
};

let printFullName = function(hometown, state) {
  console.log(this.firstName + ' ' + this.lastName, 'from', hometown, state);
};

// custom bind function
Function.prototype.myBind = function (...args) {
  let func = this;
  let obj = args[0]
  let params = args.slice(1) // First argument is the object we want to point to, the rest are the arguments we want to pass to the function

  return function () {
    func.apply(obj, params);
  };
};

let printMyName = printFullName.myBind(name, 'Hyderabad', 'Telangana');
printMyName(); // Output: Jack Smith from Hyderabad Telangana

What If We Also Pass Arguments While Calling?

let name = {
  firstName: 'Jack',
  lastName: 'Smith',
};

let printFullName = function(hometown, state, country) {
  console.log(this.firstName + ' ' + this.lastName, 'from', hometown, state, country);
};

// custom bind function
Function.prototype.myBind = function (...args) {
  let func = this;
  let obj = args[0]
  let params = args.slice(1) 

  return function () {
    func.apply(obj, params);
  };
};

let printMyName = printFullName.myBind(name, 'Hyderabad', 'Telangana');
printMyName('India'); // Output: Jack Smith from Hyderabad Telangana undefined

We want to pass 'India' during the function call, but our current polyfill ignores those.

Support Both Bind-Time and Call-Time Arguments

let name = {
  firstName: 'Jack',
  lastName: 'Smith',
};

let printFullName = function(hometown, state, country) {
  console.log(this.firstName + ' ' + this.lastName, 'from', hometown, state, country);
};

// custom bind function
Function.prototype.myBind = function (...args) {
  let func = this;
  let obj = args[0]
  let params = args.slice(1) 

  // Accepting arguments passed to printMyName
  return function (...args2) {
    func.apply(obj, [...params, ...args2]);
  };
};

let printMyName = printFullName.myBind(name, 'Hyderabad', 'Telangana');
printMyName('India'); // Output: Jack Smith from Hyderabad Telangana India

This version of myBind() correctly handles:

  • Setting this

  • Partially applying arguments

  • Receiving additional arguments at call time


Final Thoughts

With our custom implementation of bind(), we’ve essentially written a polyfill — a fallback implementation for older environments that might not support the native method.

Through this exercise, we’ve learned:

  • How the native bind() method works under the hood

  • How to handle function context (this)

  • How argument forwarding and partial application works

  • How to create your own polyfill for real-world methods


Thanks for Reading!

I hope this post helped you understand polyfills and how to build one for bind() in JavaScript. If you found this helpful, share it with your dev friends and let me know your thoughts in the comments.

Happy coding!

1
Subscribe to my newsletter

Read articles from Deepthi Purijala directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Deepthi Purijala
Deepthi Purijala

Full Stack Developer with hands-on experience of more than 1 year. Proficient in both Back-end and Front-end technologies, with a strong commitment to delivering high-quality code