Understanding the this Keyword in JavaScript

this = most confusing JavaScript concept. What this refer to? Depend on who call function. Caller = this. Understand caller, understand this.
This about this keyword and how it change.
What this Represents
this = reference to caller. Who call function? That who this is.
Simple Definition
this = the object that calling the function. Not the function itself. The thing that call it.
Real Analogy
Waiter in restaurant:
Customer say "I want coffee"
Waiter say "Who want coffee?"
Customer say "Me, I want coffee"
So "I" = the customer (the caller)
In JavaScript:
user.greet() // user calling greet()
// So "this" inside greet = user
this = who call the method.
Why this Matter
this let function know who calling it. Function can behave different for different callers.
const user1 = {
name: 'Alice',
greet() {
console.log(`Hello, I'm ${this.name}`);
}
};
const user2 = {
name: 'Bob',
greet() {
console.log(`Hello, I'm ${this.name}`);
}
};
user1.greet(); // "Hello, I'm Alice"
// this = user1
user2.greet(); // "Hello, I'm Bob"
// this = user2
Same function logic. Different this. Different output.
this in Global Context
In global scope, this = global object.
Browser Global
In browser, global object = window.
console.log(this); // window (in browser)
console.log(this === window); // true
console.log(this.innerHeight); // window.innerHeight
Top level code run in window context. So this = window.
Node.js Global
In Node.js, global object = global (or module context).
console.log(this); // {} (module context in Node.js)
// NOT global in strict module
console.log(global); // global object
Node.js = different. But usually don't use this in global scope.
Function in Global Scope
function greet() {
console.log(this);
}
greet(); // Browser: window
// Node.js: global (in strict mode: undefined)
No caller = default caller = global object.
Problem: Losing this
const user = {
name: 'Alice',
greet() {
console.log(this.name);
}
};
const func = user.greet; // Extract method
func(); // undefined (or error)
// Why?
// func() have no caller object
// So this = global or undefined
// global.name = undefined
When extract method = lose context = lose this.
this Inside Objects
this = the object calling method.
Method Call
Object call its own method. this = object.
const user = {
name: 'Alice',
age: 25,
greet() {
console.log(`I'm ${this.name}`);
},
haveBirthday() {
this.age++;
console.log(`Now ${this.age}`);
}
};
user.greet(); // this = user
// "I'm Alice"
user.haveBirthday(); // this = user
// Now 26
user.greet() = user calling greet = this = user.
Accessing Object Properties
Method use this to access own properties.
const car = {
brand: 'Toyota',
speed: 0,
accelerate() {
this.speed += 10;
console.log(`\({this.brand} speed: \){this.speed}`);
}
};
car.accelerate(); // "Toyota speed: 10"
car.accelerate(); // "Toyota speed: 20"
console.log(car.speed); // 20
this.speed refer to car's speed property.
this.brand refer to car's brand property.
Multiple Objects, Same Method
const person1 = {
name: 'Alice',
introduce() {
console.log(`I'm ${this.name}`);
}
};
const person2 = {
name: 'Bob',
introduce() {
console.log(`I'm ${this.name}`);
}
};
person1.introduce(); // "I'm Alice" (this = person1)
person2.introduce(); // "I'm Bob" (this = person2)
Different objects, same method logic. this different.
Nested Objects
const company = {
name: 'TechCorp',
owner: {
name: 'Alice',
greet() {
console.log(`I'm ${this.name}`); // this = owner (not company)
}
}
};
company.owner.greet(); // "I'm Alice"
// this = owner object (immediate caller)
this = immediate caller. Not parent object.
this Inside Functions
Function context different from object context.
Regular Function Call
function greet() {
console.log(this);
}
greet(); // Browser: window
// Node.js: undefined (strict mode) or global
// No object call it = default context
Function standalone = no object caller = this = global or undefined.
Function with Caller
const user = { name: 'Alice' };
function greet() {
console.log(`Hi, I'm ${this.name}`);
}
// Call function with user context
greet.call(user); // "Hi, I'm Alice"
// Force this = user
.call() = force who caller is.
Method vs Function
const user = {
name: 'Alice',
method() {
console.log(this.name); // this = user (method call)
},
getFunction() {
function inner() {
console.log(this.name); // this = global/undefined (function call)
}
return inner;
}
};
user.method(); // "Alice" (this = user)
const func = user.getFunction();
func(); // undefined (this = global)
Method = this = caller.
Nested function = this = global (lose context).
How Calling Context Change this
this depend on how function called. Three way to call function.
1. Method Call: obj.func()
Object call function. this = object.
const user = {
name: 'Alice',
greet() {
console.log(`Hi, ${this.name}`);
}
};
user.greet(); // this = user
2. Function Call: func()
Direct call. this = global (or undefined in strict).
function greet() {
console.log(this);
}
greet(); // this = window (browser) or undefined (strict mode)
3. Using .call() or .apply()
Explicitly set this.
const user1 = { name: 'Alice' };
const user2 = { name: 'Bob' };
function greet() {
console.log(`Hi, ${this.name}`);
}
greet.call(user1); // this = user1 → "Hi, Alice"
greet.call(user2); // this = user2 → "Hi, Bob"
.call(obj) = call function with this = obj.
Difference Visualized
user.greet()
│
└─ user calling greet
└─ this = user
greet()
│
└─ no one calling (function call)
└─ this = global
greet.call(user)
│
└─ explicitly set caller
└─ this = user
Same function. Different caller. Different this.
Arrow Functions and this
Arrow function different from regular function. Arrow function = inherit this from outer scope.
Regular Function
const user = {
name: 'Alice',
greet() {
function inner() {
console.log(this); // this = global (function call)
}
inner();
}
};
user.greet(); // this inside inner = global (lose context)
Nested function = lose this.
Arrow Function
const user = {
name: 'Alice',
greet() {
const inner = () => {
console.log(this); // this = user (inherit from greet)
};
inner();
}
};
user.greet(); // this inside inner = user (keep context)
Arrow function = inherit this from parent scope.
When Use Arrow vs Regular
const user = {
name: 'Alice',
// Regular function = this = user
greet() {
console.log(this.name); // "Alice"
},
// Arrow function = this inherit from outer
introduce: () => {
console.log(this.name); // undefined (this = window/global)
}
};
user.greet(); // "Alice" (use regular function in object)
user.introduce(); // undefined (arrow lose this context)
Rule:
- Object method = use regular function (get
this= object) - Nested callback = use arrow function (inherit
this)
Practical Example
const button = {
label: 'Click me',
// ✗ Wrong - regular function lose this
setupWrong() {
document.getElementById('btn').addEventListener('click', function() {
console.log(this.label); // this = button element (wrong)
// not this button object
});
},
// ✓ Right - arrow function keep this
setupRight() {
document.getElementById('btn').addEventListener('click', () => {
console.log(this.label); // this = button object (right)
// inherited from setupRight
});
}
};
Event listener = arrow function keep this.
Common this Mistakes
Mistake 1: Losing this in Callback
const user = {
name: 'Alice',
loadData(callback) {
setTimeout(callback, 1000);
},
fetch() {
this.loadData(function() {
console.log(this.name); // this = undefined (function call)
});
}
};
user.fetch(); // undefined (wrong)
Fix with arrow function:
user.fetch() {
this.loadData(() => {
console.log(this.name); // this = user (right)
});
}
Mistake 2: Extracting Method Without Context
const user = {
name: 'Alice',
greet() {
console.log(this.name);
}
};
const greet = user.greet; // Extract method
greet(); // undefined (lose this)
Fix with .bind():
const greet = user.greet.bind(user); // Keep this = user
greet(); // "Alice" (right)
.bind() = permanently set this.
Mistake 3: Confusing this in Constructor
function User(name) {
this.name = name;
this.greet = function() {
console.log(this.name); // this = user instance
};
}
const user = new User('Alice');
user.greet(); // "Alice" (this = user)
new = call function as constructor = this = new instance.
Mistake 4: this in Arrow Function at Object Level
// ✗ Wrong - arrow function at object level
const user = {
name: 'Alice',
greet: () => {
console.log(this.name); // this = window/global
// not user object
}
};
user.greet(); // undefined (wrong)
// ✓ Right - regular function
const user = {
name: 'Alice',
greet() {
console.log(this.name); // this = user
}
};
user.greet(); // "Alice" (right)
Object method = use regular function. Not arrow function.
Mistake 5: Assuming this Same in Nested Function
const user = {
name: 'Alice',
process() {
console.log(this.name); // "Alice" (this = user)
function inner() {
console.log(this.name); // undefined (this = global)
}
inner(); // this change!
}
};
user.process(); // "Alice" then undefined
Nested function = new context = this change.
Methods to Control this
1. Using .call()
Call function with specific this.
function greet(greeting) {
console.log(`\({greeting}, I'm \){this.name}`);
}
const user = { name: 'Alice' };
greet.call(user, 'Hi'); // "Hi, I'm Alice"
// this = user
.call(thisArg, arg1, arg2, ...) = set this and call.
2. Using .apply()
Like .call() but arguments as array.
function greet(greeting, emoji) {
console.log(`\({greeting}, I'm \){this.name} ${emoji}`);
}
const user = { name: 'Alice' };
greet.apply(user, ['Hi', '👋']); // "Hi, I'm Alice 👋"
// this = user
.apply(thisArg, [args]) = same as call, but array arguments.
3. Using .bind()
Create new function with locked this.
function greet() {
console.log(`Hi, ${this.name}`);
}
const user = { name: 'Alice' };
const boundGreet = greet.bind(user); // Create new function
boundGreet(); // "Hi, Alice"
// this permanently = user
.bind() = don't call immediately. Return new function.
.call() and .apply() = call immediately.
.bind() = return function, call later.
Practical Use: Event Listener
const user = {
name: 'Alice',
setupButton() {
const button = document.getElementById('btn');
// ✗ Lose this
// button.addEventListener('click', this.onClick);
// ✓ Keep this
button.addEventListener('click', this.onClick.bind(this));
},
onClick() {
console.log(`Clicked by ${this.name}`);
}
};
.bind(this) = keep this in event listener.
this in Different Contexts
Global Scope
console.log(this); // Browser: window, Node.js: depends on module
Inside Object Method
const obj = {
method() {
console.log(this); // this = obj
}
};
obj.method();
Inside Regular Function
function func() {
console.log(this); // undefined (strict) or global (non-strict)
}
func();
Inside Arrow Function
const arrow = () => {
console.log(this); // this = outer scope's this
};
Inside Constructor
function Constructor() {
console.log(this); // this = new instance
}
new Constructor();
Inside Class
class MyClass {
method() {
console.log(this); // this = instance
}
}
const obj = new MyClass();
obj.method();
Inside Class Arrow Method
class MyClass {
method = () => {
console.log(this); // this = instance (arrow method)
}
}
Practical Examples
Example 1: User Object
const user = {
name: 'Alice',
age: 25,
greet() {
console.log(`Hello, I'm ${this.name}`);
},
birthday() {
this.age++;
console.log(`Happy birthday! Now ${this.age}`);
},
describe() {
return `\({this.name} is \){this.age} years old`;
}
};
user.greet(); // "Hello, I'm Alice"
user.birthday(); // "Happy birthday! Now 26"
console.log(user.describe()); // "Alice is 26 years old"
// All use this = user
Example 2: Button Handler
const app = {
count: 0,
init() {
document.getElementById('btn').addEventListener(
'click',
this.handleClick.bind(this) // Keep this = app
);
},
handleClick() {
this.count++;
console.log(`Clicked ${this.count} times`);
}
};
app.init();
// Click button → "Clicked 1 times"
// Click button → "Clicked 2 times"
Without .bind(this) = this = button element (wrong).
Example 3: Method Borrowing
const user1 = { name: 'Alice' };
const user2 = { name: 'Bob' };
function greet() {
console.log(`Hi, ${this.name}`);
}
greet.call(user1); // "Hi, Alice"
greet.call(user2); // "Hi, Bob"
// Same function, different this, different output
Use .call() to borrow function with different context.
Example 4: Constructor Pattern
function User(name, age) {
this.name = name;
this.age = age;
this.greet = function() {
console.log(`I'm ${this.name}`);
};
}
const user1 = new User('Alice', 25);
const user2 = new User('Bob', 30);
user1.greet(); // "I'm Alice" (this = user1)
user2.greet(); // "I'm Bob" (this = user2)
new = create instance, set this = instance.
Example 5: React Class Component
class Counter extends React.Component {
constructor() {
super();
this.state = { count: 0 };
// Bind to keep this = component
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState({ count: this.state.count + 1 });
}
render() {
return <button onClick={this.handleClick}>Count: {this.state.count}</button>;
}
}
React = need bind or arrow function to keep this.
this Cheat Sheet
WHO CALL IT? this = WHAT?
═════════════════ ══════════════════════
obj.method() obj (the caller object)
func() undefined (strict) or global (non-strict)
new Constructor() new instance
func.call(obj) obj (explicitly set)
func.bind(obj)() obj (locked in new function)
inside arrow function outer scope's this (inherit)
inside event element (default) or bound object
listener
inside setTimeout() global (unless arrow)
Practice Assignment
1. Object methods:
// Create user object with:
// - name, email properties
// - greet() method (use this.name)
// - getEmail() method (use this.email)
// Test each method
2. Losing and finding this:
// Create object with method
// Extract method, call it (lose this)
// Fix using .bind()
// Verify this = object again
3. Arrow vs regular:
// Create object method that:
// - Use regular function for this = object
// - Use arrow function inside (inherit this)
// Test both work correctly
4. Event listener:
// Create button handler object
// Add click listener
// Use .bind(this) to keep context
// Verify this = object inside handler
5. Constructor pattern:
// Create User constructor function
// Add method using this.name
// Create 2 instances
// Call method on each (different this each time)
Quick Recap
this= reference to caller. Who call function? That whothisis.Global context =
this= window (browser) or global (Node.js).Inside object method =
this= object calling method.Inside function (not method) =
this= global or undefined (strict).Caller determine
this= same function, different caller, differentthis.Arrow function = inherit
thisfrom outer scope (don't create own).Regular function = create own
this(depend on caller)..call(obj)= call function withthis= obj (immediately)..apply(obj, [args])= like call but arguments as array (immediately)..bind(obj)= create new function withthis= obj (not immediate).Nested function = lose
this. Use arrow function or.bind().Object method = use regular function (get
this= object).Event listener = use arrow or
.bind()to keepthis.Constructor =
newsetthis= new instance.Class method =
this= instance automatically.Arrow in object = wrong. Lose
thiscontext.Remember =
this= who calling the function.
Master this = master JavaScript.
If you enjoyed this article, check out my other blogs on this profile. Connect with me: LinkedIn | GitHub | X (Twitter)



