Skip to main content
Course/Module 3/Topic 3 of 3Advanced

JavaScript This Context

Master the 'this' keyword, understand how it changes in different contexts, and learn modern patterns for handling 'this'

45 minBy Priygop TeamLast 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); // undefined

This 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

Quick Quiz — Functions & Scope

Chat on WhatsApp
Priygop - Leading Professional Development Platform | Expert Courses & Interview Prep