javascript
JavaScript 'this' Keyword
The this keyword refers to the object that is executing the current function. Its value depends on how and where the function is called.
What is 'this'?
this is a special keyword that refers to an object. Which object depends on the context:
javascript
console.log(this); // In browser: Window object
'this' in Different Contexts
Global Context
In the global scope, this refers to the global object:
javascript
console.log(this); // Window (in browsers)
function showThis() {
console.log(this); // Window (in non-strict mode)
}
Object Methods
In an object method, this refers to the object:
javascript
const person = {
name: 'John',
age: 30,
greet: function() {
console.log(`Hello, I'm ${this.name}`);
console.log(`I'm ${this.age} years old`);
}
};
person.greet(); // 'this' refers to person object
// Output: Hello, I'm John
// I'm 30 years old
Constructor Functions
In a constructor, this refers to the new object being created:
javascript
function Person(name, age) {
this.name = name;
this.age = age;
this.greet = function() {
console.log(`Hi, I'm ${this.name}`);
};
}
const john = new Person('John', 30);
john.greet(); // 'this' refers to john
Arrow Functions
Arrow functions don't have their own this. They inherit it from the parent scope:
javascript
const person = {
name: 'John',
hobbies: ['reading', 'coding'],
showHobbies: function() {
// Regular function - 'this' is person
this.hobbies.forEach(function(hobby) {
// Regular function - 'this' is undefined/Window
console.log(this.name + ' likes ' + hobby); // Error!
});
},
showHobbiesArrow: function() {
// Arrow function inherits 'this' from parent
this.hobbies.forEach(hobby => {
console.log(this.name + ' likes ' + hobby); // Works!
});
}
};
person.showHobbiesArrow();
// John likes reading
// John likes coding
Common 'this' Pitfalls
Lost Context
javascript
const person = {
name: 'John',
greet: function() {
console.log(`Hello, I'm ${this.name}`);
}
};
person.greet(); // Works: "Hello, I'm John"
const greetFunc = person.greet;
greetFunc(); // Error: 'this' is undefined or Window
Event Handlers
javascript
const button = {
text: 'Click me',
handleClick: function() {
console.log(this.text);
}
};
// 'this' will be the button element, not the object
document.querySelector('button').addEventListener('click', button.handleClick);
Explicitly Setting 'this'
call()
Call a function with a specific this value:
javascript
function greet(greeting, punctuation) {
console.log(`${greeting}, I'm ${this.name}${punctuation}`);
}
const person = { name: 'John' };
greet.call(person, 'Hello', '!');
// Output: Hello, I'm John!
apply()
Similar to call(), but arguments as an array:
javascript
function greet(greeting, punctuation) {
console.log(`${greeting}, I'm ${this.name}${punctuation}`);
}
const person = { name: 'John' };
greet.apply(person, ['Hello', '!']);
// Output: Hello, I'm John!
bind()
Create a new function with a fixed this value:
javascript
const person = {
name: 'John',
greet: function() {
console.log(`Hello, I'm ${this.name}`);
}
};
const greetFunc = person.greet.bind(person);
greetFunc(); // Works: "Hello, I'm John"
// Useful for event handlers
button.addEventListener('click', person.greet.bind(person));
Complete Example
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript 'this' Keyword</title>
<style>
body {
font-family: Arial, sans-serif;
padding: 20px;
max-width: 800px;
margin: 0 auto;
}
.demo {
background: #f5f5f5;
padding: 15px;
margin: 15px 0;
border-radius: 5px;
}
button {
background: #3498db;
color: white;
border: none;
padding: 10px 20px;
margin: 5px;
cursor: pointer;
border-radius: 4px;
}
button:hover {
background: #2980b9;
}
.output {
background: white;
padding: 10px;
margin-top: 10px;
border-radius: 3px;
font-family: monospace;
}
</style>
</head>
<body>
<h1>JavaScript 'this' Keyword Demo</h1>
<div class="demo">
<h2>Object Method</h2>
<button onclick="objectMethodDemo()">Run Demo</button>
<div id="object-output" class="output"></div>
</div>
<div class="demo">
<h2>Arrow Function vs Regular Function</h2>
<button onclick="arrowFunctionDemo()">Run Demo</button>
<div id="arrow-output" class="output"></div>
</div>
<div class="demo">
<h2>Lost Context Problem</h2>
<button onclick="lostContextDemo()">Run Demo</button>
<div id="lost-output" class="output"></div>
</div>
<div class="demo">
<h2>call, apply, bind</h2>
<button onclick="bindingDemo()">Run Demo</button>
<div id="binding-output" class="output"></div>
</div>
<div class="demo">
<h2>Event Handler Context</h2>
<button id="context-btn">Click Me</button>
<div id="event-output" class="output"></div>
</div>
<script>
function objectMethodDemo() {
const person = {
name: 'John',
age: 30,
greet: function() {
return `Hello, I'm ${this.name} and I'm ${this.age} years old`;
},
birthday: function() {
this.age++;
return `Happy birthday! Now I'm ${this.age}`;
}
};
let output = person.greet() + '<br>';
output += person.birthday() + '<br>';
output += person.greet();
document.getElementById('object-output').innerHTML = output;
}
function arrowFunctionDemo() {
const person = {
name: 'John',
hobbies: ['reading', 'coding', 'gaming'],
// Regular function
showHobbiesRegular: function() {
let results = [];
this.hobbies.forEach(function(hobby) {
// 'this' is undefined here
results.push(`Regular: ${hobby} (this.name is undefined)`);
});
return results;
},
// Arrow function
showHobbiesArrow: function() {
let results = [];
this.hobbies.forEach(hobby => {
// 'this' refers to person
results.push(`Arrow: ${this.name} likes ${hobby}`);
});
return results;
}
};
let output = '<strong>Regular function problem:</strong><br>';
output += person.showHobbiesRegular().join('<br>');
output += '<br><br><strong>Arrow function solution:</strong><br>';
output += person.showHobbiesArrow().join('<br>');
document.getElementById('arrow-output').innerHTML = output;
}
function lostContextDemo() {
const person = {
name: 'John',
greet: function() {
return `Hello, I'm ${this.name}`;
}
};
let output = '<strong>Direct call (works):</strong><br>';
output += person.greet() + '<br><br>';
output += '<strong>Lost context (broken):</strong><br>';
const greetFunc = person.greet;
try {
output += greetFunc() + '<br><br>';
} catch(e) {
output += `Error: Cannot read 'name' of undefined<br><br>`;
}
output += '<strong>Fixed with bind:</strong><br>';
const boundGreet = person.greet.bind(person);
output += boundGreet();
document.getElementById('lost-output').innerHTML = output;
}
function bindingDemo() {
function introduce(greeting, punctuation) {
return `${greeting}, I'm ${this.name}${punctuation}`;
}
const person1 = { name: 'John' };
const person2 = { name: 'Jane' };
let output = '<strong>Using call():</strong><br>';
output += introduce.call(person1, 'Hello', '!') + '<br>';
output += introduce.call(person2, 'Hi', '.') + '<br><br>';
output += '<strong>Using apply():</strong><br>';
output += introduce.apply(person1, ['Hey', '!']) + '<br>';
output += introduce.apply(person2, ['Greetings', '.']) + '<br><br>';
output += '<strong>Using bind():</strong><br>';
const johnIntro = introduce.bind(person1);
output += johnIntro('Hello', '!') + '<br>';
const janeIntro = introduce.bind(person2);
output += janeIntro('Hi', '.');
document.getElementById('binding-output').innerHTML = output;
}
// Event handler demo
const buttonHandler = {
clickCount: 0,
text: 'Button Handler Object',
// Regular function - loses context
handleClickWrong: function() {
this.clickCount++;
return `${this.text}: Clicked ${this.clickCount} times`;
},
// Bound function - maintains context
handleClickRight: function() {
this.clickCount++;
return `${this.text}: Clicked ${this.clickCount} times`;
}
};
document.getElementById('context-btn').addEventListener('click', function() {
// Using bind to maintain context
const boundHandler = buttonHandler.handleClickRight.bind(buttonHandler);
document.getElementById('event-output').innerHTML = boundHandler();
});
</script>
</body>
</html>
Preview
'this' Rules Summary
- Global context:
this= global object (Window) - Object method:
this= the object - Constructor:
this= new object being created - Arrow function:
this= inherited from parent scope - Event handler:
this= element that triggered event - call/apply/bind:
this= explicitly set value
Best Practices
- Use arrow functions - For callbacks and nested functions
- Use bind() - For event handlers and callbacks
- Avoid 'this' in global scope - Use modules or IIFE
- Be consistent - Choose a pattern and stick with it
- Use strict mode - Makes 'this' undefined instead of global object
Common Patterns
Saving 'this' Reference
javascript
const person = {
name: 'John',
hobbies: ['reading', 'coding'],
showHobbies: function() {
const self = this; // Save reference
this.hobbies.forEach(function(hobby) {
console.log(self.name + ' likes ' + hobby);
});
}
};
Using Arrow Functions
javascript
const person = {
name: 'John',
hobbies: ['reading', 'coding'],
showHobbies: function() {
this.hobbies.forEach(hobby => {
console.log(this.name + ' likes ' + hobby);
});
}
};
Binding in Constructor
javascript
class Counter {
constructor() {
this.count = 0;
// Bind in constructor
this.increment = this.increment.bind(this);
}
increment() {
this.count++;
console.log(this.count);
}
}
const counter = new Counter();
button.addEventListener('click', counter.increment); // Works!
Practice Exercise
:::practice title="Understanding 'this'" Create examples demonstrating 'this' in different contexts:
- Create an object with methods that use 'this'
- Demonstrate the lost context problem
- Fix lost context using bind()
- Compare arrow functions vs regular functions
- Use call() and apply() to set 'this'
- Create an event handler that maintains context
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>'this' Practice</title>
</head>
<body>
<h1>JavaScript 'this' Practice</h1>
<button id="counter-btn">Click to Count</button>
<div id="output"></div>
<script>
// Create a counter object
const counter = {
count: 0,
increment: function() {
// Add your code here
}
};
// Add event listener with proper 'this' binding
</script>
</body>
</html>
:::