← JavaScript core concepts for interviews

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.

js
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(); // 2

Real uses: data privacy (module pattern), function factories, memoization, event handlers that remember values, debounce and throttle.

js
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

js
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 iteration

this

this is decided by how a function is called, not where it is defined (except arrow functions).

Callthis
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 functionthis of the surrounding scope (it has no own this)
js
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

js
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 var loop print 3 3 3?
  • Why should you avoid arrow functions as object methods that use this?

Sign up free to save your progress.