Lesson 2 of 5
Scope, closures and this
2 min read
Lexical scope
A function can see variables from where it was written, not from where it's called. Inner scopes see outer variables; outer scopes can't see inner ones.
Closures
A closure is a function bundled with the variables of the scope it was created in. The function keeps access to those variables even after the outer function has returned.
function makeCounter() {
let count = 0; // private: nothing outside can touch it
return {
increment: () => ++count,
get: () => count,
};
}
const c = makeCounter();
c.increment(); c.increment();
c.get(); // 2Real uses: data privacy (module pattern), function factories, memoization, event handlers that remember values, debounce and throttle.
function debounce(fn, ms) {
let timer; // remembered by the closure between calls
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => fn(...args), ms);
};
}The classic loop question
for (var i = 0; i < 3; i++) setTimeout(() => console.log(i), 0);
// 3 3 3: one shared `i` (function-scoped), the loop finished before the callbacks ran
for (let i = 0; i < 3; i++) setTimeout(() => console.log(i), 0);
// 0 1 2: `let` creates a new binding for each iterationthis
this is decided by how a function is called, not where it is defined (except arrow functions).
| Call | this |
|---|---|
obj.method() | obj |
fn() | undefined in strict mode / modules, window in sloppy scripts |
new Fn() | the new object |
fn.call(x), fn.apply(x), fn.bind(x) | x |
| arrow function | this of the surrounding scope (it has no own this) |
const user = {
name: "Asha",
greet() { return `Hi ${this.name}`; },
greetLater() { setTimeout(() => console.log(this.name), 100); }, // arrow keeps `this`
};
const g = user.greet;
g(); // "Hi undefined": lost the object
g.call(user); // "Hi Asha"call and apply invoke immediately (apply takes arguments as an array); bind returns a new function with this fixed.
Write your own bind
Function.prototype.myBind = function (ctx, ...preset) {
const fn = this;
return function (...args) { return fn.apply(ctx, [...preset, ...args]); };
};Check yourself
- Explain a closure in one sentence and give a practical use.
- Why does the
varloop print 3 3 3? - Why should you avoid arrow functions as object methods that use
this?
Sign up free to save your progress.