JS — Closure

Closure

Normally when an function finishes executing, all the variables inside the function will gone. But because of lexical scoping, the inner function may still referencing the outer variables. So those variables will still lives in memory. This is so called `closure`.

A closure is a special kind of object that combines two things: a function, and the environment in which that function was created. The environment consists of any local variables that were in-scope at the time that the closure was created.

To be exact, the `Activation Object` (the env mentioned above) which contains all outer function’s parameters, variables etc still exists in memory. Because the inner function is still referencing it.

The inner function will becomes a closure when accessing outer function’s variables. So a closure is the inner function.

Examples

Next let’s see where can we utilize the closure.

1. ‘factory’ pattern

function makeAdder(x) {
return function(y) {
return x + y;
};
}
var add5 = makeAdder(5);
var add10 = makeAdder(10);
console.log(add5(2));  // 7
console.log(add10(2)); // 10

`makeAdder` serve as function factory to make code cleaner.

2. Event callback

Following code will change page font size when clicked:

// css
body {
font-size: 12px;
}
h1 {
font-size: 1.5em;
}
// Closure usage
function makeSizer(size) {
return function() {
document.body.style.fontSize = size + 'px';
};
}
var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);
// event callback
document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;

3. Simulate private method/variables (Module Pattern)

var counter = (function(){
var privateCounter = 0;
function changeBy(val) {
privateCounter += val;
}
return {
increment: function(){
changeBy(1);
},
decrement: function(){
changeBy(-1);
},
value: function() {
return privateCounter;
}
};
})();
console.log(counter.value()); // logs 0
counter.increment();
counter.increment();
console.log(counter.value()); // logs 2
counter.decrement();
console.log(counter.value()); // logs 1

Common mistake with closure

Closure will always get the `latest` value of outer variables

Problem:

function createFunctions() {
var result = [];
for (var i = 0; i < 10; i++) {
result[i] = function() {
return i;
}

}
return result;
}
// output: every function returns 10 instead of 1, 2, 3 ... 10

Solution:

// utilizing closure to `remember` the current `i`
function createFunctions() {
var result = [];
for (var i = 0; i < 10; i++) {
result[i] = function(num) {
return function() {
return num;
}
}(i);

}
return result;
}

Reference:

Show your support

Clapping shows how much you appreciated VLT’s story.