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!
Explanation
-
Purpose:
call()allows you to invoke a function with a specifiedthiscontext 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 (unlikeapply()) and executes the function immediately. Mention the error check ensuresthisis a function. -
Limitation: This is a basic version; real implementations might handle edge cases like
nullorundefinedcontexts 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!
Explanation
-
Purpose:
apply()is similar tocall(), 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
thisis a function and ifargsis 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 passingargumentsobject contents. Note the validation for array input. -
Limitation: This version assumes
argsis provided; a full polyfill might handlenullor 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)
Explanation
-
Purpose:
bind()creates a new function with a fixedthiscontext and optionally pre-set arguments. Unlikecall()orapply(), 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 usingapply(). -
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
customApplyfor simplicity. A full polyfill might handle constructor functions (when used withnew) 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(), orbind(). However, native implementations are preferred for performance and completeness. -
Common Theme: Note that all three methods manipulate the
thiscontext, 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-jsfor production, as they handle more edge cases. -
Browser Compatibility: Reference tools like
caniuse.comorkangax.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)