The Magic of this, call(), apply(), and bind() in JavaScript

If you have spent any time learning JavaScript, you have likely encountered the this keyword. You have also likely felt completely betrayed by it when it didn't do what you expected.
Don't worry—you are not alone. this is one of the most notoriously confusing concepts in JavaScript. But the confusion usually disappears once you understand one fundamental rule: this is not about where a regular function is written; it is entirely about who is calling the function.
Let's break down exactly what this means, how it behaves in different scenarios, and how you can take total control of it using call(), apply(), bind(), and modern Arrow Functions.
1. What exactly is this? (The Simple Explanation)
In the English language, we use pronouns like "he," "she," or "it" so we don't have to constantly repeat someone's name. In JavaScript, this acts like a pronoun for objects. It is a shortcut reference to the "current" object that is executing the code.
The golden rule to remember is: Look to the left of the dot. When a function is invoked, whatever object is standing immediately to the left of the dot is the object that this points to. If there is no dot, the default rules apply.
2. this Inside Normal Functions
What happens if you just call a regular, standalone function?
function sayHello() {
console.log(this);
}
sayHello();
Notice how we called sayHello() without any object attached to it. Because there is no specific "owner" calling the function, JavaScript defaults to the global environment.
In a web browser,
thiswill point to the Globalwindowobject.(Note: If you are writing modern JavaScript using "strict mode", JavaScript refuses to guess, and
thiswill simply beundefined.)
3. this Inside Objects
When a function is stored inside an object, we call it a method. This is where this behaves most predictably. When an object calls its own method, this points directly to that object.
const user = {
username: "CodeNinja",
role: "Admin",
printProfile: function() {
// "this" points to the "user" object
console.log(`User: \({this.username}, Role: \){this.role}`);
}
};
user.printProfile();
// Output: User: CodeNinja, Role: Admin
Because user is on the left side of the dot (user.printProfile()), user is the one "calling" the function. Therefore, this equals user.
4. The Plot Twist: Arrow Functions (=>)
Here is where modern JavaScript changes the game. Arrow functions completely ignore the "who called me?" rule.
Arrow functions do not have their own this. Instead, they borrow (or inherit) this from the surrounding code where the function was originally written. In programming terms, this is called lexical scoping. They look up to the nearest parent function that isn't an arrow function and use its this.
This is incredibly useful for things like timers or array methods where a regular function would accidentally lose its this reference:
const team = {
teamName: "The Avengers",
members: ["Iron Man", "Thor"],
showRoster: function() {
// The parent method 'showRoster' is called by 'team', so 'this' = team
this.members.forEach((member) => {
// The arrow function doesn't have its own 'this'.
// It borrows 'this' from showRoster!
console.log(`\({member} is on \){this.teamName}`);
});
}
};
team.showRoster();
// Output:
// Iron Man is on The Avengers
// Thor is on The Avengers
(If we had used a regular function(member) inside that forEach, it would have forgotten the team object and this.teamName would be undefined!)
5. Taking Control: call(), apply(), and bind()
Sometimes, the default rules get in our way. What if we want an object to use a method that belongs to a different object? We can "borrow" functions and force this to point exactly where we want it to.
JavaScript provides three built-in methods for this: call(), apply(), and bind().
call() – The Direct Borrower
The call() method allows you to execute a function immediately, but you get to dictate exactly what this should be. You pass the target object as the first argument, followed by any normal arguments the function needs, separated by commas.
const dog = { species: "Canine" };
const cat = { species: "Feline" };
function describeAnimal(name, age) {
console.log(`\({name} is a \){age}-year-old ${this.species}.`);
}
// We borrow the function and force 'this' to point to the 'dog' object
describeAnimal.call(dog, "Buddy", 3);
// Output: Buddy is a 3-year-old Canine.
// Now we do it for the cat
describeAnimal.call(cat, "Luna", 2);
// Output: Luna is a 2-year-old Feline.
apply() – The Array Specialist
apply() works exactly the same way as call(). It executes the function immediately and changes the this context.
The only difference is how it handles additional arguments. Instead of passing them one by one separated by commas, apply() takes them as a single array.
const account = { balance: 500 };
function processTransaction(type, amount) {
console.log(`${type} of $${amount} on account with $${this.balance}.`);
}
// Arguments are bundled into an array: ["Withdrawal", 50]
processTransaction.apply(account, ["Withdrawal", 50]);
// Output: Withdrawal of \(50 on account with \)500.
Pro-tip: apply() is incredibly useful when you already have your data stored in an array and need to pass it to a function that expects separate arguments.
bind() – The Future Planner
While call() and apply() execute the function right away, bind() does not.
Instead, bind() returns a brand new function with this permanently locked to the object you provided. You can then store this new function in a variable and run it later.
const server = { port: 8080 };
function connect(protocol) {
console.log(`Connecting to \({protocol} on port \){this.port}...`);
}
// We don't want to connect yet. We just want to prep the function.
const connectToMyServer = connect.bind(server, "HTTPS");
// Much later in the application...
connectToMyServer();
// Output: Connecting to HTTPS on port 8080...
bind() is essential in JavaScript, especially when passing methods as callbacks (like in React or event listeners) where the function might otherwise "lose" its original this context.
6. Your Turn: The this Assignment
Reading about code is great, but writing it is how you learn. Open your browser's developer console (F12) and try to complete this assignment:
Create the Base Object: Create an object called
robotwith a propertymodel(e.g., "T-800") and a method calleddiagnosticsthat logs"Running diagnostics on " + this.model.Borrow with
call(): Create a second object calleddronewith amodelproperty. Usecall()to make therobot's diagnostic method run, but pointthisto thedrone.Use
apply(): Write a standalone function calledupgradethat takes two arguments:part1andpart2. It should log thethis.modelgetting those parts. Useapply()to run this function on yourrobot, passing an array of parts.Use
bind(): Usebind()to create a permanently locked version of thediagnosticsmethod that is forever bound to thedrone. Store it in a variable calledcheckDroneand execute it.The Arrow Test: Inside the
robotobject, add a property calledtasks(an array of strings). Create a method calledlistTasksthat uses.forEach()and an arrow function to logthis.modelalong with each task.




