The this keyword in JavaScript is powerful but confusing. Its value changes depending on how a function is called, not where it is defined. This often leads to bugs like this being undefined, pointing to the wrong object, or referring to the global object instead of your instance.
In this guide, you’ll learn how this works in different contexts and how to fix the most common issues using bind, call, apply, and arrow functions.
1. How this Works in the Global and Function Scope
Global Scope (Non–Strict Mode)
In browsers:
console.log(this); // window
In the global scope (without strict mode), this refers to the global object (window in browsers).
Function Scope (Non–Strict Mode)
function show() {
console.log(this);
}
show(); // window (in non-strict mode)
In strict mode, this in a regular function (not as a method) is undefined:
'use strict';
function show() {
console.log(this);
}
show(); // undefined
Fix: Don’t rely on this in plain functions unless you control the call context.
2. this Inside Object Methods
When a function is called as a method of an object, this refers to that object:
const user = {
name: 'Alice',
greet() {
console.log(this.name);
}
};
user.greet(); // "Alice"
Common issue: losing this when passing methods as callbacks:
const greetFn = user.greet;
greetFn(); // undefined or error, `this` is not `user`
Fix with bind:
const boundGreet = user.greet.bind(user);
boundGreet(); // "Alice"
3. this in Event Handlers
In DOM event handlers:
button.addEventListener('click', function () {
console.log(this); // the button element
});
But with arrow functions, this is not bound to the element:
button.addEventListener('click', () => {
console.log(this); // `this` from outer scope, not button
});
Fix:
- Use regular functions if you need
thisto be the DOM element. - Or explicitly use
event.targetinstead ofthis.
button.addEventListener('click', (event) => {
console.log(event.target); // the button
});
4. Arrow Functions and Lexical this
Arrow functions do not have their own this. They capture this from the surrounding (lexical) scope:
const user = {
name: 'Bob',
greet: function () {
setTimeout(() => {
console.log(this.name); // "Bob"
}, 1000);
}
};
user.greet();
If a regular function was used in setTimeout, this would not be user:
setTimeout(function () {
console.log(this.name); // undefined or from global
}, 1000);
Fix: Use arrow functions to preserve this from the outer method, or store const self = this; and use self inside callbacks.
5. Using call, apply, and bind to Control this
call
Call a function with a specific this:
function greet() {
console.log(this.name);
}
const user = { name: 'Alice' };
greet.call(user); // "Alice"
apply
Similar to call, but arguments are passed as an array:
function introduce(greeting) {
console.log(greeting + ', ' + this.name);
}
introduce.apply(user, ['Hello']); // "Hello, Alice"
bind
bind returns a new function with this permanently set:
const boundGreet = introduce.bind(user, 'Hi');
boundGreet(); // "Hi, Alice"
Use these when you need to ensure a function always uses the correct context.
6. Common this Problems and Fixes
Problem 1: this is undefined in Class Method Callback
class Person {
constructor(name) {
this.name = name;
}
logLater() {
setTimeout(function () {
console.log(this.name);
}, 1000);
}
}
const p = new Person('Alice');
p.logLater(); // undefined
Fix with arrow function:
logLater() {
setTimeout(() => {
console.log(this.name); // "Alice"
}, 1000);
}
Or bind:
logLater() {
setTimeout(function () {
console.log(this.name);
}.bind(this), 1000);
}
Problem 2: Losing this When Passing Methods Around
const car = {
brand: 'Tesla',
show() {
console.log(this.brand);
}
};
setTimeout(car.show, 1000); // undefined
Fix:
setTimeout(car.show.bind(car), 1000); // "Tesla"
Problem 3: this Inside Nested Functions in Methods
const counter = {
value: 0,
inc() {
function inner() {
this.value++; // `this` is wrong here
}
inner();
}
};
counter.inc();
Fix 1 – Use arrow function:
inc() {
const inner = () => {
this.value++;
};
inner();
}
Fix 2 – Store reference:
inc() {
const self = this;
function inner() {
self.value++;
}
inner();
}
7. Best Practices Summary for this
- Know the call site –
thisdepends on how a function is called. - For methods used as callbacks, use
bindor arrow functions. - In class methods or object methods with async callbacks, prefer arrow functions to preserve
this. - Don’t rely on
thisinside simple utility functions; pass needed data as arguments instead. - In event handlers, use regular functions if you need
thisto be the DOM element, or useevent.target.
Mastering this makes your JavaScript code more predictable, easier to debug, and safer to reuse across different contexts.
Citations
Internal: https://savanka.com/category/savanka-helps/
External: https://developer.mozilla.org/en-US/docs/Web/JavaScript
