1. Scopes & Closures
Scopes
Scope defines where variables are accessible.
Global scope → accessible everywhere
Function scope → accessible only inside the function
Block scope (let, const) → accessible only inside {}
Example of Scopes:
let globalVar = "I am Global"; // Global Scope
function scopeExample() {
let functionVar = "I am Function Scoped"; // Function Scope
if (true) {
let blockVar = "I am Block Scoped"; // Block Scope
console.log(globalVar); // âś… Accessible
console.log(functionVar); // âś… Accessible
console.log(blockVar); // âś… Accessible
}
// console.log(blockVar); // ❌ Error: blockVar not defined here
}
scopeExample();
Closures
A closure happens when a function “remembers” variables from its outer scope, even after that scope has finished executing.
Example of Closure:
function outerFunction() {
let count = 0; // Variable inside outer scope
function innerFunction() {
count++; // inner function still has access to 'count'
console.log("Count:", count);
}
return innerFunction; // return inner function (closure)
}
const counter = outerFunction(); // outerFunction executes once
counter(); // Count: 1
counter(); // Count: 2
counter(); // Count: 3
Here’s what’s happening:
- outerFunction finishes execution. Normally, count would disappear.
- But because innerFunction closes over count, it stays alive in memory.
- Each time counter() is called, it updates the same count.
⚡ Use case of Closures:
- Data privacy (like private variables)
- Function factories
- Maintaining state without global variables
let’s go practical. Closures are super handy when you want to simulate private variables in JavaScript (since JS doesn’t have private in the classic sense).
Here’s a real-world closure example of making a counter with private state:
function createCounter() {
let count = 0; // private variable (not directly accessible outside)
return {
increment: function () {
count++;
console.log("Count:", count);
},
decrement: function () {
count--;
console.log("Count:", count);
},
getValue: function () {
return count; // expose safely
}
};
}
// Usage
const counter = createCounter();
counter.increment(); // Count: 1
counter.increment(); // Count: 2
counter.decrement(); // Count: 1
console.log("Final Value:", counter.getValue()); // Final Value: 1
// Direct access is impossible:
console.log(counter.count); // ❌ undefined (private!)
Why this works:
count is inside the createCounter scope.
The returned object’s functions close over count, keeping it alive.
Outside code can’t touch count directly — only through the methods we expose.
👉 This pattern is widely used in module design, authentication tokens, shopping carts, etc. to keep sensitive data safe.
2. this Keyword & Context
Objectives
- Difference between: Global this, Object method this, Arrow function this (lexical binding).
- Why? Angular uses class components heavily; misunderstanding this leads to broken method bindings.
1. Global this
In strict mode (default in ES6+), this in the global scope is undefined.
In non-strict mode, this is the global object (window in browsers, global in Node).
console.log(this);
// In browser: window (non-strict mode)
// In strict mode: undefined
2. Object Method this
When a method is called on an object, this refers to the object itself.
const user = {
name: "Mitu",
greet: function () {
console.log("Hello, " + this.name);
}
};
user.greet(); // âś… "Hello, Mitu"
But if you detach the method, this
breaks:
const detached = user.greet;
detached();
// ❌ In strict mode: undefined
// ❌ In browser non-strict: "Hello, undefined" (because `this` = window)
3. Arrow Function this (Lexical Binding)
Arrow functions don’t have their own this. Instead, they lexically bind this from their surrounding scope.
const user = {
name: "Mitu",
greet: () => {
console.log("Hello, " + this.name);
}
};
user.greet(); // ❌ "Hello, undefined"
Why?
Because this in an arrow function doesn’t point to the object — it points to whatever this was in the scope where the function was created (in this case, global this).
âś… Why Angular Uses Arrow Functions in Callbacks
In Angular, class components heavily rely on this to access properties/methods.
Problem: Losing this binding
@Component({...})
export class AppComponent {
name = "Mitu";
ngOnInit() {
setTimeout(function () {
console.log(this.name);
// ❌ ERROR: Cannot read property 'name' of undefined
}, 1000);
}
}
Here, this inside setTimeout is not the class, but the global context.
Solution: Use Arrow Functions (lexical this)
@Component({...})
export class AppComponent {
name = "Mitu";
ngOnInit() {
setTimeout(() => {
console.log(this.name);
// âś… "Mitu"
}, 1000);
}
}
🔑 Quick Summary
- Global this → window (non-strict) or undefined (strict).
- Object Method this → depends on the caller (can break if detached).
- Arrow Function this → lexically bound; inherits this from surrounding scope.
👉 That’s why Angular encourages arrow functions in callbacks — to preserve this and avoid method-binding issues.
Top comments (0)