Advanced Concepts: this, Proxy, Generators, Iterators
The this Keyword
this refers to the object currently executing the function. Its value depends on how the function is called.
// 1. Global context: this = window (browser) or global (Node)
console.log(this); // Window in browser
// 2. Regular function: this = caller (undefined in strict mode)
function show() {
console.log(this); // Window (or undefined in strict)
}
// 3. Method: this = object the method is called on
const obj = {
name: "Reet",
greet() { console.log(this.name); } // "Reet"
};
obj.greet();
// 4. Arrow function: inherits this from enclosing scope
const obj2 = {
name: "Reet",
greet: () => console.log(this.name) // undefined! (arrow has no own this)
};
// 5. Event handler: this = element that triggered event
btn.addEventListener("click", function() {
console.log(this); // the button element
});
// 6. new: this = new instance being created
function Person(name) { this.name = name; }
const p = new Person("Reet"); // this = p
Explicit this Binding: call, apply, bind
function greet(greeting, punctuation) {
return `${greeting}, ${this.name}${punctuation}`;
}
const user = { name: "Reet" };
// call: invoke immediately, args one-by-one
greet.call(user, "Hello", "!"); // "Hello, Reet!"
// apply: invoke immediately, args as array
greet.apply(user, ["Hello", "!"]); // "Hello, Reet!"
// bind: returns a NEW function with this permanently bound
const boundGreet = greet.bind(user, "Hi");
boundGreet("."); // "Hi, Reet."
boundGreet("!"); // "Hi, Reet!"
// Common use: preserving this in callbacks
class Timer {
constructor() { this.seconds = 0; }
start() {
setInterval(this.tick.bind(this), 1000);
// Or use arrow: setInterval(() => this.tick(), 1000)
}
tick() { this.seconds++; }
}
Generators (ES6)
Functions that can be paused and resumed.
function* counter(start = 0) {
while (true) {
yield start++; // yield pauses and returns value
}
}
const gen = counter(1);
gen.next(); // { value: 1, done: false }
gen.next(); // { value: 2, done: false }
gen.next(); // { value: 3, done: false }
// Finite generator
function* range(start, end, step = 1) {
for (let i = start; i < end; i += step) {
yield i;
}
}
for (const n of range(0, 10, 2)) {
console.log(n); // 0, 2, 4, 6, 8
}
// Sending values to generator
function* calculator() {
const x = yield "Enter x:";
const y = yield "Enter y:";
yield x + y;
}
const calc = calculator();
calc.next(); // { value: "Enter x:", done: false }
calc.next(10); // { value: "Enter y:", done: false }
calc.next(5); // { value: 15, done: false }
Iterators and Iterables
// An iterable has [Symbol.iterator]() method
// An iterator has .next() method
// Making a custom iterable
const range = {
from: 1,
to: 5,
[Symbol.iterator]() {
let current = this.from;
const last = this.to;
return {
next() {
if (current <= last) {
return { value: current++, done: false };
}
return { value: undefined, done: true };
}
};
}
};
for (const n of range) {
console.log(n); // 1, 2, 3, 4, 5
}
[...range]; // [1, 2, 3, 4, 5]
Proxy
Intercepts and customizes fundamental operations on objects.
const handler = {
get(target, prop) {
return prop in target ? target[prop] : `Property '${prop}' not found`;
},
set(target, prop, value) {
if (typeof value !== "number") throw new TypeError("Numbers only!");
target[prop] = value;
return true; // required in strict mode
}
};
const proxy = new Proxy({}, handler);
proxy.age = 25; // works (25 is a number)
proxy.age; // 25
proxy.name; // "Property 'name' not found"
proxy.score = "A"; // TypeError: Numbers only!
Reflect
Companion to Proxy. Provides methods for interceptable JS operations.
Reflect.get(obj, "name"); // like obj.name
Reflect.set(obj, "name", "Alice"); // like obj.name = "Alice"
Reflect.has(obj, "name"); // like "name" in obj
Reflect.deleteProperty(obj, "name"); // like delete obj.name
Reflect.ownKeys(obj); // keys + symbols
// Common in Proxy handlers for default behavior
const handler = {
get(target, prop, receiver) {
console.log(`Getting ${prop}`);
return Reflect.get(target, prop, receiver);
}
};
MCQ — Advanced Concepts
Q1: What does call vs apply differ in?
A) call is async, apply is sync
B) call passes args one-by-one; apply passes them as an array ✅
C) apply binds permanently; call does not
D) No difference
Answer: B
Q2: What does bind() return?
A) The result of calling the function
B) A new function with this permanently set ✅
C) A Promise
D) The original function modified
Answer: B
Q3: What does yield do in a generator?
A) Returns a value and terminates the function
B) Pauses execution and returns a value; resumes on next .next() call ✅
C) Skips to the next iteration
D) Throws an error
Answer: B
Q4: What is an iterable in JavaScript?
A) Any object with a length property
B) An object with a [Symbol.iterator]() method ✅
C) Any array-like object
D) An object that extends Array
Answer: B
Q5: What is a Proxy used for?
A) Making HTTP requests B) Deep-cloning objects C) Intercepting and customizing object operations (get, set, delete, etc.) ✅ D) Creating private class fields
Answer: C
Q6: What is this inside an arrow function defined as a method?
A) The object the method belongs to
B) undefined
C) The enclosing lexical this (not the object) ✅
D) The global object
Answer: C — Arrow functions don't have their own this. This is why arrow functions should NOT be used as object methods.