JavaScript
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax and Types
-
Section 3: Strings and Numbers in Depth
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Objects and Arrays
-
Section 7: Maps, Sets, and Symbols
-
Section 8: Asynchronous JavaScript
-
Section 9: Object-Oriented and Prototypes
-
Section 10: The DOM
-
Section 11: Browser APIs
-
Section 12: Modern JavaScript (ES2015-ES2025)
-
Section 13: Functional Programming Patterns
-
Section 14: Error Handling and Debugging
-
Section 15: Testing
-
Section 16: Accessibility for JavaScript Developers
-
Section 17: Internationalization and Localization
-
Section 18: Performance
-
Section 19: Node.js Fundamentals
-
Section 20: Regular Expressions
-
Section 21: Design Patterns in JavaScript
-
Section 22: Security Basics
-
Section 23: Data Structures and Algorithms in JavaScript
-
Section 24: Practical Projects
-
Section 25: More Advanced Async Patterns
-
Section 26: More Object and Class Practice
-
Section 27: Working with Dates and Internationalization
-
Section 28: Web Components
-
Section 29: More DOM and Browser Practice
-
Section 30: Build Tooling for Vanilla JavaScript
-
Section 31: More Practice Projects
-
Section 32: Interview and Algorithm Practice
-
Section 33: Error Objects (MDN Reference)
-
Section 34: TypedArrays and Binary Data
-
Section 35: Reflection and Metaprogramming (MDN Reference)
-
Section 36: More Global Functions (MDN Reference)
32: The this Keyword in Depth
If you've spent any time debugging JavaScript, you've likely hit a wall where this is undefined or, even worse, it's pointing to the window object when you're absolutely certain it should be pointing to your class instance. It's one of the most frustrating parts of the language because it feels unpredictable.
The Myth: "this" Always Refers to the Object it's Defined In
The most common misconception I see is the belief that this is lexically scoped—meaning, that it's "locked" to the object where you wrote the function. It feels intuitive. If you write a method inside a User object, you assume this will always be that user. But JavaScript doesn't work that way. Let's look at why this fails.
const player = {
name: 'Sora',
greet() {
console.log(`Hello, my name is ${this.name}`);
}
};
player.greet(); // "Hello, my name is Sora" - This works!
const shout = player.greet;
shout(); // "Hello, my name is undefined" (or an error in strict mode)
Wait, what happened? We took the exact same function and just gave it a different name. But when we called shout(), this vanished. This is because this isn't about where the function was defined; it's about how the function was called.
The Reality: It's All About the Call Site
In JavaScript, the value of this is determined at runtime. I like to tell my juniors to look at the "call site"—the actual line of code where the function is executed. If there is a dot to the left of the function call, this is the object before that dot. In player.greet(), the object is player. In shout(), there is no dot, so this defaults to the global object (or undefined if you're using strict mode).
There are four main rules that determine the value of this, in order of priority:
- New Binding: If you use the
newkeyword,thisis the brand new object being created. - Explicit Binding: If you use
.call(),.apply(), or.bind(), you are manually telling JavaScript exactly whatthisshould be. - Implicit Binding: If you call a function as a method (e.g.,
obj.method()),thisis the object before the dot. - Default Binding: If none of the above apply,
thisis the global window (orundefined).
Forcing the Issue with call, apply, and bind
Sometimes you need to "borrow" a method from one object and use it on another. This is where explicit binding comes in. I use this occasionally when I want to use a utility method from a prototype without inheriting the whole class.
const admin = {
name: 'Boss',
permissions: ['read', 'write', 'delete'],
checkAccess(role) {
console.log(`${this.name} is checking access for ${role}...`);
}
};
const guest = { name: 'Visitor' };
// We borrow checkAccess from admin but force 'this' to be guest
admin.checkAccess.call(guest, 'guest-role');
// Output: "Visitor is checking access for guest-role..."
.call() and .apply() execute the function immediately. .bind() is different; it doesn't run the function right away. Instead, it returns a new function that is permanently bound to the object you specified. This is a lifesaver when passing methods as callbacks to event listeners.
Arrow Functions: The Lexical Exception
Now, here is the "cheat code." Arrow functions do not have their own this. They don't follow any of the rules I just listed. Instead, they capture the this value of the surrounding code (the lexical scope) at the moment they are created.
If you're inside a class method and you need to run a setTimeout, a regular function will lose its this context because setTimeout calls the function without a "dot" (default binding). An arrow function, however, will remember the this from the method it was written inside. Honestly, this is why arrow functions are used for 90% of callbacks in modern JS.
📋 Practical Task
Fixing the Shopping Cart Timeout Bug
You are building a shopping cart where a "Session Expiring" warning appears after 3 seconds. However, the current code is broken because the this context is lost when the timer fires, resulting in an error when trying to access this.cartName.
Your Task: Modify the startExpiryTimer method so that the console.log correctly prints the name of the cart. You can solve this using either .bind() or by converting the callback to an arrow function.
const ShoppingCart = {
cartName: "Guest Checkout Cart",
items: [],
startExpiryTimer() {
console.log("Timer started...");
// BUG: 'this' will be undefined or the window object here
setTimeout(function() {
console.log(`Warning: ${this.cartName} is about to expire!`);
}, 3000);
}
};
ShoppingCart.startExpiryTimer();
There are no comments for now.