DEV Community

ZeeshanAli-0704
ZeeshanAli-0704

Posted on

Polyfill - call, apply & bind

I'll provide the polyfill code for each method with a brief explanation in separate sections. I'll keep the focus on simplicity and clarity so you can easily explain them during an interview.

1. Polyfill for call()

Code

// Polyfill for Function.prototype.call
if (!Function.prototype.customCall) {
  Function.prototype.customCall = function (currentThis = {}, ...args) {
    // Check if 'this' is a callable function
    if (typeof this !== 'function') {
      throw new Error(`${this} is not a function`);
    }
    // Attach the function to the provided context
    currentThis.tempFn = this;
    // Call the function with the arguments and return result
    const result = currentThis.tempFn(...args);
    // Clean up the temporary property
    delete currentThis.tempFn;
    return result;
  };
}

// Example usage
const obj = { name: 'John' };
function greet(message) {
  return `${message}, ${this.name}!`;
}
console.log(greet.customCall(obj, 'Hello')); // Outputs: Hello, John!
Enter fullscreen mode Exit fullscreen mode

Explanation

  • Purpose: call() allows you to invoke a function with a specified this context and individual arguments. It’s used for function borrowing, letting one object use a method from another.
  • How It Works: The polyfill attaches the function (this) to the provided context (currentThis) as a temporary property, calls it with the spread arguments (...args), and then removes the temporary property to avoid pollution.
  • Key Interview Point: Highlight that call() passes arguments individually (unlike apply()) and executes the function immediately. Mention the error check ensures this is a function.
  • Limitation: This is a basic version; real implementations might handle edge cases like null or undefined contexts more robustly by falling back to the global object.

2. Polyfill for apply()

Code

// Polyfill for Function.prototype.apply
if (!Function.prototype.customApply) {
  Function.prototype.customApply = function (currentThis = {}, args = []) {
    // Check if 'this' is a callable function
    if (typeof this !== 'function') {
      throw new Error(`${this} is not a function`);
    }
    // Check if args is an array
    if (!Array.isArray(args)) {
      throw new Error('Arguments must be an array');
    }
    // Attach the function to the provided context
    currentThis.tempFn = this;
    // Call the function with the array of arguments and return result
    const result = currentThis.tempFn(...args);
    // Clean up the temporary property
    delete currentThis.tempFn;
    return result;
  };
}

// Example usage
const obj = { name: 'Jane' };
function greet(message, punctuation) {
  return `${message}, ${this.name}${punctuation}`;
}
console.log(greet.customApply(obj, ['Hi', '!'])); // Outputs: Hi, Jane!
Enter fullscreen mode Exit fullscreen mode

Explanation

  • Purpose: apply() is similar to call(), but it accepts arguments as an array instead of individual parameters. It’s useful when the number of arguments is dynamic.
  • How It Works: The polyfill checks if this is a function and if args is an array. It then attaches the function to the context, calls it with the spread operator on the array, and cleans up.
  • Key Interview Point: Emphasize the difference from call()apply() takes an array of arguments, making it handy for scenarios like passing arguments object contents. Note the validation for array input.
  • Limitation: This version assumes args is provided; a full polyfill might handle null or non-array inputs with additional checks.

3. Polyfill for bind()

Code

// Polyfill for Function.prototype.bind
if (!Function.prototype.customBind) {
  Function.prototype.customBind = function (currentThis, ...boundArgs) {
    // Check if 'this' is a callable function
    if (typeof this !== 'function') {
      throw new Error(`${this} is not a function`);
    }
    // Store the original function
    const originalFn = this;
    // Return a new function that can be invoked later
    return function (...callArgs) {
      // Call the original function with bound context and combined arguments
      return originalFn.customApply(currentThis, boundArgs.concat(callArgs));
    };
  };
}

// Example usage
const obj = { name: 'Alice' };
function greet(message) {
  return `${message}, ${this.name}!`;
}
const boundGreet = greet.customBind(obj, 'Hey');
console.log(boundGreet()); // Outputs: Hey, Alice!
console.log(boundGreet('Hello')); // Outputs: Hey, Alice! (ignores additional args in this case)
Enter fullscreen mode Exit fullscreen mode

Explanation

  • Purpose: bind() creates a new function with a fixed this context and optionally pre-set arguments. Unlike call() or apply(), it doesn’t execute immediately—it returns a bound function for later use.
  • How It Works: The polyfill captures the original function and bound arguments (boundArgs), then returns a new function. When invoked, it combines the bound arguments with any new arguments (callArgs) and calls the function with the fixed context using apply().
  • Key Interview Point: Stress that bind() is for delayed execution and permanent context binding (e.g., for event handlers). Explain how it merges preset and new arguments.
  • Limitation: This version uses customApply for simplicity. A full polyfill might handle constructor functions (when used with new) and edge cases like unbound calls.

General Interview Talking Points

  • Why Polyfills?: Explain that polyfills like these ensure compatibility with older browsers lacking native support for call(), apply(), or bind(). However, native implementations are preferred for performance and completeness.
  • Common Theme: Note that all three methods manipulate the this context, a core concept in JavaScript, and are crucial for functional programming patterns like borrowing methods.
  • Testing: Mention you’d test these polyfills in different environments or use libraries like core-js for production, as they handle more edge cases.
  • Browser Compatibility: Reference tools like caniuse.com or kangax.github.io/compat-table/es6/ to check support (though these methods are widely supported now, polyfills are still a good learning exercise).

Top comments (0)