A closure is a function that *remembers* the variables from the scope where it was defined, even after that outer scope has finished executing.
```javascript
function makeCounter() {
let count = 0; // outer variable
return function () { // inner function — a closure
count += 1;
return count;
};
}
const counter = makeCounter();
counter(); // 1
counter(); // 2
counter(); // 3
```
After `makeCounter()` returns, you'd expect `count` to be gone. But `counter` still holds a reference to it — that's the closure.
**The scope chain**
When a function looks up a variable, it walks up the scope chain: local scope → enclosing function scope → module scope → global scope. A closure captures the enclosing scope at the time the inner function is *defined*, not called.
```javascript
const x = 'global';
function outer() {
const x = 'outer';
function inner() {
console.log(x); // 'outer' — captured from enclosing scope
}
return inner;
}
const fn = outer();
fn(); // 'outer' (not 'global')
```
**Closure vs global variable**
Both store state that persists across calls. The difference: a closure keeps state *private* to the function that created it, while a global variable is shared by everything.
```javascript
// Global state - anyone can accidentally modify it
let globalCount = 0;
function incrementGlobal() { globalCount++; return globalCount; }
// Closure state - only the returned function can change it
function makeSecureCounter() {
let count = 0;
return () => ++count;
}
const secureCount = makeSecureCounter();
// count is not accessible from outside
```
**Multiple closures sharing the same outer variable**
Two closures created in the same scope share the *same* binding, not independent copies:
```javascript
function makeUpDown() {
let n = 0;
return {
up: () => ++n,
down: () => --n,
get: () => n,
};
}
const counter = makeUpDown();
counter.up(); // 1
counter.up(); // 2
counter.down(); // 1
counter.get(); // 1 — both up and down see the same n
```
Factory functions, the var-in-loop bug, and the module pattern
**Factory functions**
A factory is a function that creates and returns other functions. Each returned function closes over its own private state:
```javascript
function makeAdder(x) {
return (y) => x + y; // x is captured
}
const add5 = makeAdder(5);
const add10 = makeAdder(10);
add5(3) // 8
add10(3) // 13 — independent from add5, has its own x
```
**The classic `var`-in-loop bug**
This is one of the most famous JavaScript gotchas. All closures in the loop share the *same* `var i` binding, which ends up as 3 after the loop finishes:
```javascript
const fns = [];
for (var i = 0; i < 3; i++) {
fns.push(() => i); // all close over the same 'i'
}
fns[0](); // 3 (not 0!)
fns[1](); // 3
fns[2](); // 3
```
**Fix 1: use `let`** — `let` creates a new binding per iteration:
```javascript
const fns = [];
for (let i = 0; i < 3; i++) {
fns.push(() => i); // each iteration has its own 'i'
}
fns[0](); // 0
fns[1](); // 1
fns[2](); // 2
```
**Fix 2: IIFE** (immediately invoked function expression) — captures the value at that moment:
```javascript
const fns = [];
for (var i = 0; i < 3; i++) {
fns.push(((captured) => () => captured)(i));
}
fns[0](); // 0
```
The IIFE solution is historical — before `let` existed (ES6). Today, always use `let` (or `const`) in loops.
**The module pattern**
Before ES6 modules, closures were used to create private state: an IIFE returns an object with public methods while keeping implementation details hidden.
```javascript
const bankAccount = (() => {
let balance = 0; // private
return {
deposit: (n) => { balance += n; },
withdraw: (n) => { balance = Math.max(0, balance - n); },
getBalance: () => balance,
};
})();
bankAccount.deposit(100);
bankAccount.withdraw(30);
bankAccount.getBalance(); // 70
// balance is not accessible directly
```
Closure patterns: memoize, once, partial, and memory pitfalls
**Memoize** — cache the result of expensive calls:
```javascript
function memoize(fn) {
const cache = new Map();
return function (...args) {
const key = JSON.stringify(args);
if (cache.has(key)) return cache.get(key);
const result = fn.apply(this, args);
cache.set(key, result);
return result;
};
}
const fib = memoize(function (n) {
if (n <= 1) return n;
return fib(n - 1) + fib(n - 2);
});
fib(40); // instant
```
**Once** — run a function only on the first call:
```javascript
function once(fn) {
let called = false;
let result;
return function (...args) {
if (!called) {
called = true;
result = fn.apply(this, args);
}
return result;
};
}
const initDB = once(() => { console.log('DB connected'); return 'ok'; });
initDB(); // 'DB connected' -> 'ok'
initDB(); // (silent) -> 'ok' — same result, no side effects
```
**Partial application** — pre-fill some arguments:
```javascript
function partial(fn, ...presetArgs) {
return (...laterArgs) => fn(...presetArgs, ...laterArgs);
}
const multiply = (a, b) => a * b;
const double = partial(multiply, 2);
const triple = partial(multiply, 3);
double(5); // 10
triple(5); // 15
```
**Closure pitfall: unintended memory retention**
Closures keep the entire outer scope alive. If the outer scope holds large objects, they won't be garbage collected:
```javascript
function processLargeData(data) {
const hugeArray = new Array(1_000_000).fill(data); // large
return () => hugeArray.length; // closure keeps hugeArray alive forever
}
// Fix: only capture what you need
function processLargeData(data) {
const hugeArray = new Array(1_000_000).fill(data);
const size = hugeArray.length; // extract the value
return () => size; // closure only captures 'size', not hugeArray
}
```
**setTimeout with closures**
Callbacks passed to `setTimeout` close over the variables at the time the callback is created:
```javascript
for (let i = 1; i <= 3; i++) {
setTimeout(() => console.log(i), i * 1000); // prints 1, 2, 3 (with let)
}
// With var: would print 4, 4, 4
```
Write a function makeCounter() that returns a new counter object. Each call to makeCounter() should create an independent counter with increment() and getValue() methods.
Write a function once(fn) that takes a function and returns a new function that can only be called once. On the first call it executes fn and returns the result; subsequent calls return undefined.
Write a function memoize(fn) that wraps a function and caches its results. If the function is called again with the same argument, return the cached result instead of calling fn again.
function memoize(fn) {
}
const slowSquare = memoize(n => {
console.log(`computing ${n}...`);
return n * n;
});
console.log(slowSquare(4));
console.log(slowSquare(4));
console.log(slowSquare(5));
Solution
function memoize(fn) {
const cache = {};
return function(arg) {
if (arg in cache) return cache[arg];
cache[arg] = fn(arg);
return cache[arg];
};
}
const slowSquare = memoize(n => {
console.log(`computing ${n}...`);
return n * n;
});
console.log(slowSquare(4));
console.log(slowSquare(4));
console.log(slowSquare(5));
Write a function makeBankAccount(initialBalance) that returns an object with deposit(amount) and withdraw(amount) methods and a getBalance() method. The balance should not be directly accessible from outside.
Write a function withCallCount(fn) that wraps a function and returns an object with a call method and a getCount() method. call() invokes the original function and getCount() returns how many times it was called.
function withCallCount(fn) {
}
const wrapped = withCallCount(x => x * 2);
wrapped.call(5);
wrapped.call(10);
wrapped.call(3);
console.log(wrapped.getCount());
Write a function partial(fn, ...preArgs) that takes a function and some arguments, and returns a new function that prepends those arguments to any future arguments when calling fn.
function partial(fn, ...preArgs) {
}
function add(a, b, c) {
return a + b + c;
}
const add10 = partial(add, 10);
console.log(add10(5, 3));
console.log(add10(1, 2));
Solution
function partial(fn, ...preArgs) {
return function(...laterArgs) {
return fn(...preArgs, ...laterArgs);
};
}
function add(a, b, c) {
return a + b + c;
}
const add10 = partial(add, 10);
console.log(add10(5, 3));
console.log(add10(1, 2));
No split tab
Cookie preferences
We use necessary cookies to run the site. With your permission, we can also save your site preferences and use analytics and advertising cookies to understand usage and support the project.
Open tools in tabs.Exercises, IDE tools, and trainers stay available as site tabs.
Switch without losing context.Move between explanations, code, and utilities while keeping your place.
Use the sidebar as your map.The left panels hold navigation, settings, files, libraries, and tool controls.
PythonJavaScriptSQLite
One IDE, three practical modes
Python in the browser.Run small scripts, try libraries, and practice API requests without installing anything.
JavaScript for quick experiments.Test browser-friendly code and compare ideas next to your learning materials.
SQLite for data practice.Open the database explorer to inspect tables, write queries, and learn SQL workflows locally.
TopicIDE
Work side by side with split tabs
Keep instructions visible.Open an exercise or reference page beside the IDE instead of jumping back and forth.
Compare tools while you learn.Place regex checks, explanations, and code experiments next to each other when the task needs it.
Close the split when you are done.The workspace returns to a single focused tab, and your open site tabs remain available.
Code Typing Trainer
Or plain text
This trainer is designed for a physical keyboard.Open this section on a laptop or desktop with a wide screen. Touch typing practice will not work correctly on a phone.
Speed: 0 chars/min
0 words/min
Best speed (60s): 0 chars/min
0 words/min
Errors: 0
Total time: 0.0 s
To practice touch typing, avoid looking at your physical keyboard.