If you’ve been writing JavaScript for a while, you’ve probably stumbled upon the mysterious keyword this.
Sometimes it refers to the object you expect — and sometimes… it doesn’t.
Let’s uncover its secrets.
Global Object
When you call a function on nothing, you’re calling it on the global object.
- In browser JavaScript, that’s typically
window(the browser window) - In Node.js, that’s
global
console.log('Hi!')
window.console.log('Hi!') // same thing
OOP and this
When you call a function that comes from inside a class or object, the value of this depends on how you call it.
If you call it without the object reference, the context (this) is lost and becomes undefined (in strict mode).
This is often referred to as “losing the this context.”
Luckily, there are ways to control what this means — even when JavaScript forgets.
call()
Sometimes you need to say: “call this function on this object.”
That’s what call() is for!
const user1 = { name: "Ana" };
const user2 = { name: "Lucas" };
function sayHello() {
console.log(`Olá ${this.name}`);
}
sayHello.call(user1); // Olá Ana
sayHello.call(user2); // Olá Lucas
apply()
The only difference between call() and apply() is how they receive arguments.
-
call()takes arguments individually -
apply()takes them as an array
function sum(a, b, c) {
return a + b + c;
}
const nums = [1, 2, 3];
console.log(sum.call(null, 1, 2, 3)); // 6
console.log(sum.apply(null, nums)); // 6
bind()
The bind() method creates a new function with a fixed this value.
It’s useful when you pass a method as a callback and don’t want to lose its original context.
const user = {
name: "Karen",
showName() {
console.log(this.name);
},
};
setTimeout(user.showName.bind(user), 1000);
// Without bind → undefined
// With bind → "Karen"
If the function doesn’t use this, passing it is optional.
Binding with Event Listeners
When functions are called indirectly by JavaScript — for example, through event listeners, timers, or callbacks — JS automatically sets the this value depending on context.
- In event listeners →
thisrefers to the element that triggered the event. - In regular functions or callbacks →
thisis oftenundefined(in strict mode) orwindow(in non-strict mode).
const button = document.querySelector('button');
button.addEventListener('click', function () {
console.log(this); // 👉 the button element
});
However, if you use an arrow function, this behaves differently (see below).
bind() with Timers
Callbacks like setTimeout() and setInterval() don’t preserve context.
That’s why methods from objects can “lose” their this.
const counter = {
value: 0,
increment() {
this.value++;
console.log(this.value);
},
};
setInterval(counter.increment.bind(counter), 1000);
// Without bind → NaN
// With bind → 1, 2, 3...
Arrow Functions
Arrow functions don’t have their own this.
Instead, they inherit this from their surrounding scope (lexical binding).
This makes them perfect for preserving context in callbacks.
const user = {
name: "Karen",
showNameLater() {
setTimeout(() => {
console.log(this.name);
}, 1000);
},
};
user.showNameLater(); // 👉 "Karen"
If we used a regular function inside setTimeout, this would be undefined.
Key Takeaways
Make sure you don’t lose your this.
-
thisis a reserved keyword whose value is determined only at the moment of function execution - If you don’t call a function yourself (e.g., callbacks, event listeners, timers), make sure JavaScript knows what
thisshould refer to - Use
bind()or arrow functions to safely keep context
💡 Pro tip:
When debugging, console.log(this) at different points is often the fastest way to understand what context you’re working with.
Top comments (0)