2754. Bind Function to Context 🔒
Problem Description
This problem asks you to implement a polyfill for JavaScript's bind
method. The goal is to add a method called bindPolyfill
to all functions that mimics the behavior of the native bind
method.
When bindPolyfill
is called on a function with an object as an argument, it should return a new function where the this
context is permanently set to the provided object.
Key requirements:
- Add
bindPolyfill
method to theFunction.prototype
so all functions have access to it - The method takes one parameter: an object that will become the
this
context - It returns a new function that, when called, executes the original function with the bound
this
context - The returned function should pass along any arguments it receives to the original function
- You cannot use the built-in
Function.bind
method
Example behavior:
Without binding:
function f() {
console.log('My context is ' + this.ctx);
}
f(); // Output: "My context is undefined"
With binding using bindPolyfill
:
function f() {
console.log('My context is ' + this.ctx);
}
const boundFunc = f.bindPolyfill({ "ctx": "My Object" });
boundFunc(); // Output: "My context is My Object"
The solution uses Function.prototype
to add the method to all functions. Inside bindPolyfill
, it returns an arrow function that captures the original function (this
) and the object to bind. When the returned function is called, it uses call
to invoke the original function with the specified this
context and forwards any arguments using the spread operator (...args
).
Intuition
To understand how to build a bind
polyfill, we need to think about what binding actually does: it creates a new function that "remembers" a specific this
context, no matter how that function is eventually called.
The key insight is that we need to create a closure - a function that captures and preserves values from its surrounding scope. When bindPolyfill
is called, we want to:
- Capture the original function - We need to preserve reference to the function we're binding (which is
this
inside thebindPolyfill
method) - Capture the target object - We need to preserve the object that should become the new
this
context - Return a wrapper function - This new function will call the original with the correct context
Since we can't use the native bind
, we need another way to explicitly set the this
context when calling a function. JavaScript provides call
and apply
methods for this exact purpose. The call
method allows us to invoke a function with a specific this
value.
Why use an arrow function for the returned wrapper? Arrow functions are ideal here because:
- They automatically capture variables from their enclosing scope (creating the closure we need)
- They don't have their own
this
binding, sothis
inside the arrow function refers to thethis
frombindPolyfill
(which is our original function)
The spread operator (...args
) handles the forwarding of arguments elegantly - whatever arguments are passed to the bound function get collected into an array and then spread back out when calling the original function.
This approach essentially manually implements what the native bind
does under the hood: creating a closure that preserves both the function and its intended context, then using call
to apply that context when the function is invoked.
Solution Approach
The implementation extends the Function.prototype
to add the bindPolyfill
method to all functions in JavaScript. Here's how the solution works step by step:
1. Extending Function.prototype
Function.prototype.bindPolyfill = function (obj) {
// implementation here
};
By adding a method to Function.prototype
, every function in JavaScript automatically inherits this method. This allows any function to call bindPolyfill
.
2. The method signature
The method accepts a single parameter obj
which is the object that will become the this
context for the bound function.
3. Returning a new function
return (...args) => {
return this.call(obj, ...args);
};
The method returns an arrow function that:
- Uses the rest parameter syntax
...args
to collect all arguments passed to it into an array - Inside this arrow function,
this
refers to the original function (the onebindPolyfill
was called on) because arrow functions don't have their ownthis
binding
4. Using call
to set context
this.call(obj, ...args)
The call
method is used to invoke the original function with:
obj
as thethis
context (first parameter tocall
)...args
spread out as individual arguments (remaining parameters tocall
)
5. TypeScript declarations
type Fn = (...args) => any;
declare global {
interface Function {
bindPolyfill(obj: Record<any, any>): Fn;
}
}
These TypeScript declarations:
- Define
Fn
as a function type that can accept any arguments and return any value - Extend the global
Function
interface to include thebindPolyfill
method - Specify that
bindPolyfill
takes a record (object) and returns a function
The beauty of this solution is its simplicity - it leverages JavaScript's closure mechanism and the call
method to achieve the same effect as the native bind
method in just a few lines of code.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a concrete example to see how the bindPolyfill
solution works:
// Step 1: Define a function that uses 'this'
function greet(greeting, punctuation) {
return greeting + ', ' + this.name + punctuation;
}
// Step 2: Create an object to bind as context
const person = { name: 'Alice' };
// Step 3: Call bindPolyfill
const boundGreet = greet.bindPolyfill(person);
When bindPolyfill
is called:
- Inside
bindPolyfill
,this
refers to thegreet
function - The
obj
parameter contains{ name: 'Alice' }
- An arrow function is created and returned:
(...args) => { return this.call(obj, ...args); // 'this' is captured as the greet function // 'obj' is captured as { name: 'Alice' } }
Now when we call the bound function:
// Step 4: Call the bound function with arguments const result = boundGreet('Hello', '!');
Here's what happens internally:
- The arrow function receives arguments:
['Hello', '!']
- These are collected into
args
array via rest parameters - The arrow function executes
greet.call(person, 'Hello', '!')
- Inside
greet
,this
is nowperson
object - The function accesses
this.name
which is'Alice'
- Returns:
'Hello, Alice!'
Even if we try to change the context later:
const anotherPerson = { name: 'Bob' }; boundGreet.call(anotherPerson, 'Hi', '?'); // Still returns "Hi, Alice?"
The bound context remains person
because it was captured in the closure when bindPolyfill
was first called. The arrow function always uses the original obj
(person) when calling the original function, regardless of how the bound function itself is invoked.
Solution Implementation
1# Type definition: Fn represents a callable that accepts any arguments and returns any type
2# In Python, we use Callable from typing module for this purpose
3from typing import Any, Callable, Dict
4
5# Define type alias for a function that accepts any arguments and returns any type
6Fn = Callable[..., Any]
7
8# Custom implementation of bind functionality as a polyfill
9# Since Python doesn't have prototypes like JavaScript, we'll create a wrapper class
10class FunctionWithBindPolyfill:
11 """Wrapper class to add bindPolyfill method to functions"""
12
13 def __init__(self, func: Callable):
14 """Initialize with the original function"""
15 self.original_function = func
16
17 def bindPolyfill(self, obj: Dict[Any, Any]) -> Fn:
18 """
19 Custom polyfill method that mimics the behavior of native bind()
20 Takes an object to be used as 'this' context and returns a new function
21
22 Args:
23 obj: Dictionary/object to be used as the context for the function
24
25 Returns:
26 A new function with the bound context
27 """
28 # Store reference to the original function
29 original_function = self.original_function
30
31 # Return a new function that maintains the bound context
32 def bound_function(*args: Any, **kwargs: Any) -> Any:
33 """
34 Inner function that calls the original with the specified context
35
36 Args:
37 *args: Variable positional arguments
38 **kwargs: Variable keyword arguments
39
40 Returns:
41 Result of calling the original function with bound context
42 """
43 # In Python, we simulate 'this' binding by passing obj as first argument
44 # if the original function expects it (like a method would)
45 if hasattr(original_function, '__self__'):
46 # If it's already a bound method, call normally
47 return original_function(*args, **kwargs)
48 else:
49 # Simulate JavaScript's call() by passing obj as self if needed
50 # For regular functions, just call with arguments
51 try:
52 # Try calling as a method with obj as self
53 return original_function(obj, *args, **kwargs)
54 except TypeError:
55 # If that fails, call as a regular function
56 return original_function(*args, **kwargs)
57
58 # Return the new bound function
59 return bound_function
60
61
62# Alternative implementation using a decorator approach
63def add_bind_polyfill(func: Callable) -> Any:
64 """
65 Decorator to add bindPolyfill method to a function
66
67 Args:
68 func: The function to enhance with bindPolyfill
69
70 Returns:
71 The function with bindPolyfill method added
72 """
73 # Create a wrapper class instance for the function
74 wrapper = FunctionWithBindPolyfill(func)
75
76 # Copy the original function's attributes
77 wrapper.__name__ = func.__name__
78 wrapper.__doc__ = func.__doc__
79
80 # Make the wrapper callable like the original function
81 wrapper.__call__ = func
82
83 # Return the wrapper with bindPolyfill method
84 return wrapper
85
1import java.lang.reflect.Method;
2import java.util.function.Function;
3
4/**
5 * Interface representing a function that accepts variable arguments and returns Object
6 * This is the Java equivalent of TypeScript's Fn type
7 */
8@FunctionalInterface
9interface Fn {
10 Object apply(Object... args);
11}
12
13/**
14 * Custom function wrapper class that provides bind functionality
15 * Since Java doesn't allow extending native classes like Function,
16 * we create a wrapper class to achieve similar behavior
17 */
18class FunctionWrapper {
19 // The original method to be invoked
20 private final Method method;
21 // The object instance that owns the method
22 private final Object instance;
23
24 /**
25 * Constructor to wrap a method with its instance
26 * @param method The method to be wrapped
27 * @param instance The object instance that owns the method
28 */
29 public FunctionWrapper(Method method, Object instance) {
30 this.method = method;
31 this.instance = instance;
32 }
33
34 /**
35 * Implementation of bindPolyfill that creates a new function with a fixed 'this' context
36 * @param obj The object to be used as 'this' context when the function is called
37 * @return A new function (Fn) that calls the original method with the bound context
38 */
39 public Fn bindPolyfill(Object obj) {
40 // Store reference to the original method
41 final Method originalFunction = this.method;
42
43 // Return a new function that maintains the bound context
44 // Using lambda expression which is equivalent to arrow function in TypeScript
45 return (Object... args) -> {
46 try {
47 // Make the method accessible in case it's private
48 originalFunction.setAccessible(true);
49 // Call the original method with the specified 'this' context (obj) and arguments
50 return originalFunction.invoke(obj, args);
51 } catch (Exception e) {
52 // Handle reflection exceptions
53 throw new RuntimeException("Error invoking bound method", e);
54 }
55 };
56 }
57}
58
1#include <functional>
2#include <any>
3#include <vector>
4#include <memory>
5
6// Template class to implement a custom bind polyfill
7// This mimics JavaScript's Function.prototype.bind behavior
8template<typename ReturnType, typename... Args>
9class BindPolyfill {
10private:
11 // Store the original function
12 std::function<ReturnType(Args...)> original_function;
13 // Store the bound object context
14 std::any bound_context;
15
16public:
17 // Constructor that takes a function and an object to bind as context
18 BindPolyfill(std::function<ReturnType(Args...)> func, std::any obj)
19 : original_function(func), bound_context(obj) {}
20
21 // Overloaded function call operator to make this callable
22 // Applies the original function with the bound context
23 ReturnType operator()(Args... args) {
24 // In C++, we simulate the 'this' binding by passing the bound object
25 // as the first parameter if needed, or using it as context
26 return original_function(args...);
27 }
28};
29
30// Helper function to create a bindPolyfill
31// Takes a callable and an object to bind as context
32template<typename Func, typename Context>
33auto bindPolyfill(Func func, Context obj) {
34 // Return a lambda that captures both the function and the context
35 // This lambda forwards all arguments to the original function
36 return [func, obj](auto&&... args) -> decltype(auto) {
37 // If the function is a member function, it would need special handling
38 // For general functions, we simply call with the provided arguments
39 return func(std::forward<decltype(args)>(args)...);
40 };
41}
42
43// Alternative implementation using std::bind-like approach
44template<typename ReturnType, typename ClassType, typename... Args>
45class MemberFunctionBinder {
46private:
47 // Pointer to member function
48 ReturnType (ClassType::*member_func_ptr)(Args...);
49 // Pointer to the object instance
50 ClassType* object_instance;
51
52public:
53 // Constructor taking member function pointer and object pointer
54 MemberFunctionBinder(ReturnType (ClassType::*func)(Args...), ClassType* obj)
55 : member_func_ptr(func), object_instance(obj) {}
56
57 // Call operator that invokes the member function on the bound object
58 ReturnType operator()(Args... args) {
59 // Call the member function with the bound object instance
60 return (object_instance->*member_func_ptr)(args...);
61 }
62};
63
64// Utility function to create a member function binder
65// Binds a member function to a specific object instance
66template<typename ReturnType, typename ClassType, typename... Args>
67auto bindPolyfill(ReturnType (ClassType::*func)(Args...), ClassType* obj) {
68 // Return a lambda that captures the member function and object
69 return [func, obj](Args... args) -> ReturnType {
70 // Invoke the member function on the bound object
71 return (obj->*func)(args...);
72 };
73}
74
1// Type definition for a function that accepts any arguments and returns any type
2type Fn = (...args: any[]) => any;
3
4// Extend the global Function interface to include our custom bindPolyfill method
5declare global {
6 interface Function {
7 // Custom polyfill method that mimics the behavior of native bind()
8 // Takes an object to be used as 'this' context and returns a new function
9 bindPolyfill(obj: Record<any, any>): Fn;
10 }
11}
12
13// Implementation of bindPolyfill on Function prototype
14// This method creates a new function with a fixed 'this' context
15Function.prototype.bindPolyfill = function (obj: Record<any, any>): Fn {
16 // Store reference to the original function
17 const originalFunction = this;
18
19 // Return a new arrow function that maintains the bound context
20 return (...args: any[]): any => {
21 // Call the original function with the specified 'this' context and arguments
22 return originalFunction.call(obj, ...args);
23 };
24};
25
Time and Space Complexity
Time Complexity: O(1)
- The
bindPolyfill
method creates and returns a new arrow function, which is a constant-time operation - The returned function itself, when invoked, uses
call()
to execute the original function with the bound context - The
call()
method hasO(1)
overhead for setting up the execution context - The actual time complexity when the bound function executes depends on the original function's implementation, but the binding operation itself is
O(1)
Space Complexity: O(1)
- The
bindPolyfill
method creates a closure that captures:- The reference to the original function (
this
) - The reference to the object (
obj
)
- The reference to the original function (
- These are fixed-size references regardless of input size
- The arrow function returned occupies constant space
- The
...args
spread operator in the returned function will create a new array when the function is called, but this is part of the execution space, not the binding space - Overall space complexity for the binding operation is
O(1)
Common Pitfalls
1. Losing the Original Function Reference
One of the most common mistakes is not properly capturing the original function reference before returning the new bound function. Developers often make this error:
Incorrect Implementation:
Function.prototype.bindPolyfill = function(obj) {
return function(...args) {
return this.call(obj, ...args); // ERROR: 'this' is undefined or wrong context
};
};
Problem: Using a regular function instead of an arrow function causes this
inside the returned function to refer to the wrong context (or be undefined in strict mode) rather than the original function.
Solution: Use an arrow function to preserve the lexical this
:
Function.prototype.bindPolyfill = function(obj) {
return (...args) => {
return this.call(obj, ...args); // Correct: 'this' refers to the original function
};
};
2. Not Handling Constructor Calls
The native bind
method handles the case where the bound function is called with new
. The polyfill should account for this:
Incomplete Implementation:
Function.prototype.bindPolyfill = function(obj) {
return (...args) => {
return this.call(obj, ...args);
};
};
Problem: Arrow functions cannot be used as constructors, so new boundFunc()
will throw an error.
Better Solution:
Function.prototype.bindPolyfill = function(obj) {
const originalFunc = this;
return function(...args) {
if (new.target) {
// Called with 'new'
return new originalFunc(...args);
}
return originalFunc.call(obj, ...args);
};
};
3. Forgetting to Return the Result
A subtle but critical mistake is forgetting to return the result of the function call:
Incorrect:
Function.prototype.bindPolyfill = function(obj) {
return (...args) => {
this.call(obj, ...args); // Missing return statement
};
};
Problem: The bound function will always return undefined
regardless of what the original function returns.
Solution: Always return the result:
Function.prototype.bindPolyfill = function(obj) {
return (...args) => {
return this.call(obj, ...args); // Properly returns the result
};
};
4. Not Preserving Partial Application
The native bind
method supports partial application (pre-filling arguments), but basic polyfills often miss this:
Limited Implementation:
Function.prototype.bindPolyfill = function(obj) {
return (...args) => {
return this.call(obj, ...args);
};
};
Full Implementation with Partial Application:
Function.prototype.bindPolyfill = function(obj, ...boundArgs) {
return (...args) => {
return this.call(obj, ...boundArgs, ...args);
};
};
5. TypeScript Type Safety Issues
When implementing in TypeScript, not properly typing the return value can lead to loss of type information:
Poor Typing:
Function.prototype.bindPolyfill = function(obj: any): any {
return (...args: any[]) => {
return this.call(obj, ...args);
};
};
Better Typing:
type Fn<T = any> = (...args: any[]) => T;
declare global {
interface Function {
bindPolyfill<T>(this: Fn<T>, obj: Record<any, any>): Fn<T>;
}
}
Function.prototype.bindPolyfill = function<T>(obj: Record<any, any>): Fn<T> {
return (...args: any[]): T => {
return this.call(obj, ...args);
};
};
These pitfalls highlight the importance of understanding JavaScript's execution context, the differences between arrow functions and regular functions, and the full behavior of the native bind
method when creating a polyfill.
Which algorithm is best for finding the shortest distance between two points in an unweighted graph?
Recommended Readings
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!