Skip to main content

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.