JavaScript This Context
Master the 'this' keyword, understand how it changes in different contexts, and learn modern patterns for handling 'this'
45 min•By Priygop Team•Last updated: Feb 2026
This Context Fundamentals
The 'this' keyword refers to the object that is currently executing the code. Its value depends on how a function is called, not how it's defined. Understanding 'this' is crucial for object-oriented programming in JavaScript.
This in Different Contexts
Example
// This in global context
console.log(this); // Window (in browser) or global (in Node.js)
// This in function context
function regularFunction() {
console.log(this);
}
regularFunction(); // Window/global (in non-strict mode)
// This in method context
const obj = {
name: 'Object',
method() {
console.log(this.name);
}
};
obj.method(); // "Object"
// This in constructor context
function Person(name) {
this.name = name;
this.greet = function() {
console.log(`Hello, I'm ${this.name}`);
};
}
const person = new Person('John');
person.greet(); // "Hello, I'm John"
// This in arrow function context
const arrowObj = {
name: 'Arrow Object',
regularMethod() {
console.log('Regular method:', this.name);
setTimeout(function() {
console.log('Callback function:', this.name); // undefined
}, 100);
setTimeout(() => {
console.log('Arrow function:', this.name); // "Arrow Object"
}, 200);
}
};
arrowObj.regularMethod();
// This in event handler context
const button = document.createElement('button');
button.textContent = 'Click me';
button.addEventListener('click', function() {
console.log(this); // The button element
});
// This with call, apply, bind
const user = {
name: 'John',
greet(greeting) {
console.log(`${greeting}, ${this.name}!`);
}
};
const admin = {
name: 'Admin'
};
user.greet.call(admin, 'Hello'); // "Hello, Admin!"
user.greet.apply(admin, ['Hi']); // "Hi, Admin!"
const boundGreet = user.greet.bind(admin);
boundGreet('Welcome'); // "Welcome, Admin!"
// This in class context
class User {
constructor(name) {
this.name = name;
}
greet() {
console.log(`Hello, ${this.name}!`);
}
static create(name) {
return new User(name);
}
}
const user1 = new User('Alice');
user1.greet(); // "Hello, Alice!"
// This in module context
// In a module file, 'this' refers to undefined (strict mode)
console.log(this); // undefinedThis Context Patterns
Example
// Common this context patterns
// 1. Method borrowing
const calculator = {
add(a, b) {
return a + b;
},
multiply(a, b) {
return a * b;
}
};
const numbers = [1, 2, 3, 4, 5];
const sum = numbers.reduce(calculator.add, 0);
console.log(sum); // 15
// 2. Partial application with bind
function multiply(a, b) {
return a * b;
}
const double = multiply.bind(null, 2);
const triple = multiply.bind(null, 3);
console.log(double(5)); // 10
console.log(triple(5)); // 15
// 3. Event handler binding
class EventHandler {
constructor() {
this.count = 0;
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.count++;
console.log(`Clicked ${this.count} times`);
}
// Alternative: Arrow function method
handleMouseOver = () => {
console.log(`Mouse over, count: ${this.count}`);
}
}
// 4. Callback context preservation
class DataProcessor {
constructor() {
this.data = [];
}
addItem(item) {
this.data.push(item);
}
processItems() {
// Preserve this context in callbacks
this.data.forEach(function(item) {
console.log('Processing:', item);
}.bind(this));
// Or use arrow function
this.data.forEach(item => {
console.log('Processing:', item);
});
}
}
// 5. Method chaining with this
class ChainableObject {
constructor() {
this.value = 0;
}
add(n) {
this.value += n;
return this; // Return this for chaining
}
multiply(n) {
this.value *= n;
return this;
}
subtract(n) {
this.value -= n;
return this;
}
getValue() {
return this.value;
}
}
const chain = new ChainableObject();
const result = chain.add(5).multiply(2).subtract(3).getValue();
console.log(result); // 7
// 6. Factory functions with this
function createCounter() {
return {
count: 0,
increment() {
this.count++;
return this;
},
decrement() {
this.count--;
return this;
},
getValue() {
return this.count;
}
};
}
const counter = createCounter();
console.log(counter.increment().increment().decrement().getValue()); // 1
// 7. This in async context
class AsyncProcessor {
constructor() {
this.data = [];
}
async addItem(item) {
this.data.push(item);
return this;
}
async process() {
const results = [];
for (let item of this.data) {
const result = await this.processItem(item);
results.push(result);
}
return results;
}
async processItem(item) {
return new Promise(resolve => {
setTimeout(() => {
resolve(`Processed: ${item}`);
}, 100);
});
}
}
// 8. This in generator functions
function* numberGenerator() {
let i = 0;
while (true) {
yield i++;
}
}
const gen = numberGenerator();
console.log(gen.next().value); // 0
console.log(gen.next().value); // 1
// 9. This in proxy objects
const handler = {
get(target, prop, receiver) {
console.log(`Accessing property: ${prop}`);
return Reflect.get(target, prop, receiver);
},
set(target, prop, value, receiver) {
console.log(`Setting property: ${prop} = ${value}`);
return Reflect.set(target, prop, value, receiver);
}
};
const proxy = new Proxy({ name: 'John' }, handler);
console.log(proxy.name); // Logs: "Accessing property: name"
proxy.age = 30; // Logs: "Setting property: age = 30"Practice Exercise: This Context
Example
// Exercise: Build a Chainable API
class ChainableAPI {
constructor() {
this.operations = [];
this.result = null;
}
select(selector) {
this.operations.push({ type: 'select', selector });
return this;
}
filter(callback) {
this.operations.push({ type: 'filter', callback });
return this;
}
map(callback) {
this.operations.push({ type: 'map', callback });
return this;
}
sort(comparator) {
this.operations.push({ type: 'sort', comparator });
return this;
}
limit(n) {
this.operations.push({ type: 'limit', n });
return this;
}
execute(data) {
let result = [...data];
for (let operation of this.operations) {
switch (operation.type) {
case 'select':
result = result.filter(item =>
Object.keys(operation.selector).every(key =>
item[key] === operation.selector[key]
)
);
break;
case 'filter':
result = result.filter(operation.callback);
break;
case 'map':
result = result.map(operation.callback);
break;
case 'sort':
result = result.sort(operation.comparator);
break;
case 'limit':
result = result.slice(0, operation.n);
break;
}
}
this.result = result;
return this;
}
getResult() {
return this.result;
}
reset() {
this.operations = [];
this.result = null;
return this;
}
}
// Exercise: Build a Method Decorator
function logMethod(target, propertyKey, descriptor) {
const originalMethod = descriptor.value;
descriptor.value = function(...args) {
console.log(`Calling ${propertyKey} with args:`, args);
const result = originalMethod.apply(this, args);
console.log(`${propertyKey} returned:`, result);
return result;
};
return descriptor;
}
function validateMethod(validator) {
return function(target, propertyKey, descriptor) {
const originalMethod = descriptor.value;
descriptor.value = function(...args) {
if (!validator(...args)) {
throw new Error(`Invalid arguments for ${propertyKey}`);
}
return originalMethod.apply(this, args);
};
return descriptor;
};
}
// Exercise: Build a Context Manager
class ContextManager {
constructor() {
this.contexts = new Map();
this.currentContext = null;
}
createContext(name, data = {}) {
const context = {
name,
data: { ...data },
parent: this.currentContext,
children: new Set()
};
if (this.currentContext) {
this.currentContext.children.add(context);
}
this.contexts.set(name, context);
return context;
}
enterContext(name) {
const context = this.contexts.get(name);
if (!context) {
throw new Error(`Context '${name}' not found`);
}
this.currentContext = context;
return context;
}
exitContext() {
if (this.currentContext) {
this.currentContext = this.currentContext.parent;
}
}
getCurrentContext() {
return this.currentContext;
}
setData(key, value) {
if (this.currentContext) {
this.currentContext.data[key] = value;
}
}
getData(key) {
if (this.currentContext) {
return this.currentContext.data[key];
}
return undefined;
}
}
// Test the exercises
const api = new ChainableAPI();
const data = [
{ id: 1, name: 'John', age: 30 },
{ id: 2, name: 'Jane', age: 25 },
{ id: 3, name: 'Bob', age: 35 },
{ id: 4, name: 'Alice', age: 28 }
];
const result = api
.select({ age: 30 })
.map(item => ({ ...item, status: 'active' }))
.limit(2)
.execute(data)
.getResult();
console.log('API Result:', result);
// Test method decorators
class Calculator {
@logMethod
@validateMethod((a, b) => typeof a === 'number' && typeof b === 'number')
add(a, b) {
return a + b;
}
@logMethod
multiply(a, b) {
return a * b;
}
}
const calc = new Calculator();
console.log(calc.add(5, 3)); // Logs method calls and returns 8
// Test context manager
const contextManager = new ContextManager();
contextManager.createContext('user', { id: 1, name: 'John' });
contextManager.enterContext('user');
contextManager.setData('session', 'active');
console.log('Current context:', contextManager.getCurrentContext().name);
console.log('User data:', contextManager.getData('name'));Try It Yourself — Functions & Scope
Try It Yourself — Functions & ScopeHTML
HTML Editor
✓ ValidTab = 2 spaces
HTML|40 lines|1826 chars|✓ Valid syntax
UTF-8