Hello! Let's continue our exploration of JavaScript's execution context.
In the previous lesson, we established the four rules that dynamically determine the value of this in a regular function, with explicit binding (call, apply, bind) holding the second-highest priority. You saw that when you extract a method from an object and pass it as a callback, it "loses" its original context, often leading to bugs.
This lesson focuses on the solution to that problem: explicit binding. Your goal is to describe how bind, call, and apply can be used to explicitly set the this context of a function. We'll move beyond simply knowing they exist to deeply understanding their mechanics, differences, and practical applications. Given your interest in how things work internally, we will also implement our own versions—polyfills—for these methods to solidify your mental model.
Explicit Binding: call, apply, and bind
These three methods are built into every function in JavaScript via Function.prototype. Their primary job is to invoke a function while giving you explicit control over what the this keyword will refer to inside that function's execution.
Here is a high-level comparison:
| Method | Invokes Function? | How Arguments are Passed | Returns |
|---|---|---|---|
**.call()** |
Yes, immediately | Individually, separated by commas | Function's result |
**.apply()** |
Yes, immediately | As a single array | Function's result |
**.bind()** |
No | Individually, for partial application | A new function |
This diagram provides a great visual summary of how each method is used.

Now, let's watch a quick overview that demonstrates the core difference in syntax and behavior between these three methods.
Javascript Interview Questions ( Call, Bind and Apply ) - Polyfills, Output Based, Explicit Binding
This video from RoadsideCoder provides a concise demonstration of call, apply, and bind, setting the stage for our deeper dive.
Watch from 00:00:39 to 00:04:54. Pay close attention to: How call passes arguments individually. How apply passes arguments as an array. How bind returns a new function that can be called later.
Let's break down the distinct characteristics and use cases for each.
1. Function.prototype.call(thisArg, arg1, arg2, ...)
As you saw, call invokes the function immediately. Its first argument is the object to be used as the this context, followed by any arguments for the function, passed individually.
A primary use case is method borrowing, where you use a method from one object on another, completely unrelated object. This is possible because JavaScript methods are just functions that happen to be properties of an object and are designed to operate on a this context.
const user = {
name: 'Alice'
};
function greet(greeting, punctuation) {
console.log(`${greeting}, my name is ${this.name}${punctuation}`);
}
// We "borrow" the greet function to run in the context of 'user'
greet.call(user, 'Hello', '!'); // Logs: "Hello, my name is Alice!"
// This is also common for using Array methods on array-like objects
function logArguments() {
// The 'arguments' object is array-like, but not a true array.
// It doesn't have a .forEach method.
console.log('Arguments passed:');
// We can borrow Array.prototype.forEach and apply it to 'arguments'.
Array.prototype.forEach.call(arguments, (item) => {
console.log(`- ${item}`);
});
}
logArguments('A', 'B', 'C');
// Logs:
// Arguments passed:
// - A
// - B
// - C
2. Function.prototype.apply(thisArg, [argsArray])
apply is nearly identical to call: it also invokes the function immediately and sets the this context. The only difference is that it accepts the function's arguments as a single array.
Apply = Array.
This is most useful when the arguments you want to pass are already in an array or when the number of arguments is dynamic.
this, call, apply & bind - 33 JavaScript Concepts
The article 'this, call, apply & bind' explains a classic use case for apply: finding the maximum value in an array with Math.max. While modern JavaScript has the spread operator (...) which often makes this specific use case cleaner, understanding the apply pattern is still valuable.
Read the section titled 'apply() — Apply with Array'. Focus on the 'Classic Use Case: Finding Max/Min' to see how apply unpacks an array of numbers into arguments for a function that doesn't natively accept arrays.
3. Function.prototype.bind(thisArg, arg1, arg2, ...)
bind is fundamentally different from call and apply. It does not execute the function. Instead, it creates and returns a new function where the this keyword is permanently bound to the provided thisArg.
This is the definitive solution to the "lost context" problem we discussed in the last lesson. When you pass a callback, you don't want it to execute immediately; you want the receiving function (e.g., setTimeout, addEventListener) to execute it later. bind allows you to prepare a function with the correct context ahead of time.
class Logger {
constructor(prefix) {
this.prefix = prefix;
}
log(message) {
console.log(`${this.prefix}: ${message}`);
}
}
const myLogger = new Logger('APP_INFO');
myLogger.log('Initializing...'); // Works fine, 'this' is myLogger
// Problem: "Lost context"
// When setTimeout calls this function, 'this' will be the global object or undefined.
setTimeout(myLogger.log, 1000, 'Delayed message'); // Throws an error in strict mode
// Solution: Use bind to create a new function with 'this' permanently bound to myLogger
const boundLog = myLogger.log.bind(myLogger);
setTimeout(boundLog, 1000, 'Delayed message'); // Works correctly! Logs: "APP_INFO: Delayed message"
Another powerful feature of bind is partial application. Any arguments you provide after thisArg are pre-filled in the new function.
function multiply(a, b) {
return a * b;
}
// Create a new function 'double' where 'a' is always 2
const double = multiply.bind(null, 2); // 'this' is not used, so we pass null
console.log(double(5)); // 10 (equivalent to multiply(2, 5))
console.log(double(10)); // 20 (equivalent to multiply(2, 10))
Edge Cases & Deeper Understanding
To truly master these methods, let's look at some specific scenarios that often appear in technical interviews.
Javascript Interview Questions ( Call, Bind and Apply ) - Polyfills, Output Based, Explicit Binding
Let's explore some more advanced scenarios, including practical applications of apply, the immutability of a bound context, and how arrow functions interact with these methods.
Watch the following segments from the RoadsideCoder video: Appending Arrays (11:32 - 13:35): A clever use of apply with Array.prototype.push. Bind Chaining (15:49 - 16:50): What happens when you try to bind a function that is already bound? This reveals a key property of bind. Arrow Functions (21:12 - 23:14): How do call, apply, and bind affect arrow functions? This connects back to our previous lesson on lexical this.
Key Observations:
- Bind is Permanent: As seen in the "bind chaining" example, once a function's
thisis bound, it cannot be changed again bycall,apply, or even anotherbind. The firstbindwins. - Arrow Functions are Immune:
call,apply, andbindhave no effect on thethisvalue of an arrow function. Arrow functions don't have their ownthisto be bound; they always inherit it from their lexical scope. This makes them predictable and robust.
Under the Hood: Polyfills
Now for the most insightful part of our lesson. To understand how these methods truly work, we will implement them from scratch. A "polyfill" is a piece of code that provides the functionality of a modern feature in older environments that don't support it natively. For us, it's a powerful learning tool.
Since all functions can access .call, .apply, and .bind, we know they must live on Function.prototype. This provides a great bridge to our next topic on prototypal inheritance.
The core idea behind these polyfills is surprisingly simple:
- Temporarily attach the function you want to call as a method onto the target
contextobject. - Invoke that method on the context object (so
thisis implicitly bound to it). - Remove the temporary method.
Javascript Interview Questions ( Call, Bind and Apply ) - Polyfills, Output Based, Explicit Binding
This final video segment is the most crucial for your goal of understanding the mechanics. The presenter will build polyfills for myCall, myApply, and myBind step-by-step. Follow his logic closely.
Watch carefully from 23:14 to 32:14. I will summarize the key implementation details below. Polyfill for call (23:14): Notice how this inside the polyfill refers to the function it's being called on (e.g., purchaseCar). The function is then attached to the context object and executed. Polyfill for apply (26:49): See how this is almost identical to myCall, but with an added check to ensure the second argument is an array. Polyfill for bind (28:12): This is the most complex. The key is that it returns a new function. This new function, when invoked, will use the logic from myApply or myCall to execute with the bound context and a combination of pre-filled and newly provided arguments.
Let's summarize the polyfill logic.
myCall Implementation Strategy:
Function.prototype.myCall = function(context, ...args) {
// 'this' here is the function myCall was invoked on.
// We attach it to the context object.
context.fn = this;
// We invoke it, so inside 'fn', 'this' now refers to 'context'.
const result = context.fn(...args);
// Clean up the temporary property.
delete context.fn;
return result;
}
myBind Implementation Strategy:
Function.prototype.myBind = function(context, ...args) {
// 'this' here is the original function. Let's capture it.
const originalFunc = this;
// Return a new function.
return function(...newArgs) {
// When this new function is called, invoke the original function
// using 'apply' or 'call' to set the correct context and combine
// the pre-filled arguments with any new ones.
return originalFunc.apply(context, [...args, ...newArgs]);
}
}
This exercise of re-implementing these methods strips away the "magic" and reveals them as clever applications of JavaScript's fundamental object and function behaviors.
Conclusion
You now have a deep, mechanical understanding of call, apply, and bind. They are not just utilities but essential tools for managing one of JavaScript's most powerful and confusing features: the this keyword.
Key Takeaways:
- Immediate vs. Deferred:
callandapplyinvoke a function immediately with a specifiedthiscontext.bindreturns a new function that is permanently bound to athiscontext, ready to be called later. - Argument Handling:
calltakes arguments as a comma-separated list.applytakes arguments as a single array.bindcan take initial arguments for partial application. - Core Use Cases: Use
call/applyfor method borrowing and immediate invocation with a dynamic context. Usebindfor callbacks (event handlers,setTimeout) and creating specialized functions through partial application. - Immutability: Once
bindis used, the function's context cannot be changed. Arrow functions are lexically bound and their context is also immutable.
What's Next?
In building our polyfills, we attached our custom methods to Function.prototype to make them available on all functions. This leads us directly to our next topic: JavaScript's prototypal inheritance model. We will explore how objects inherit properties and methods from their prototypes and how the prototype chain enables powerful patterns of code reuse.