JavaScript Closures
- a function that makes use of variables defined in outer functions that have previously returned
- if the inner function doesn't use any of the external variables all we have is a nested
function
function outer () {
let data = 'closures are ';
return function inner() {
let innerData = 'awesome';
return data + innerData;
}
}
outer(); // returns the definition of the inner function
outer()(); // "closures are awesome"
function outer (a) {
return function inner (b) {
// the inner function is making use of the variable 'a', which was defined in an outer
// function called `outer` --- by the time this is called, that outer function has returned
// this function called `inner` is a closure!
return a + b;
}
}
outer(5)(5); // 10
let storeOuter = outer(5);
storeOuter(10); // 15
- we have to 'return' the inner function for this to work
- we can either call the inner function right away by using an extra
()
or we can store the
result of the function in a variable (very similar to how bind works)
- we do NOT have to give the inner function a name --- it can be anonymous (just done in examples
for clarity while learning)
Use Closures to Create Private Variables
- in other languages, there exists support for variables that can't be modified externally
, called private variables --- not built-in in JS
function counter () {
let count = 0;
return function () {
return ++count; // using prefix operator so it goes up to 1 the first time it is
// called
}
}
counter1 = counter();
counter1(); // 1
counter1(); // 2
counter2 = counter();
counter2(); // 1
counter2(); // 2
counter1(); // 3 --- not affected by counter2
count; // ReferenceError: count is not defined --- becasue it is private
// --- it is private becasue it can't be accessed outside of
// `counter` because it isn't scoped outside the function
function classRoom () {
let instructors = ['Colt', 'Elie'];
return {
getInstructors: function () { // this is a closure
return instructors;
},
addInstructor: function (instructor) { // this is also a closure
instructors.push(instructor);
return instructors;
}
}
}
course1 = classRoom();
course1.getInstructors(); // ["Elie", "Colt"]
course1.addInstructor('Ian'); // ["Elie", "Colt", "Ian"]
course1.getInstructors(); // ["Elie", "Colt", "Ian"]
course2 = classRoom();
course2.getInstructors(); // ["Elie", "Colt"] --- not affected by course1
// we also have NO access to the instructors variable, which makes it private
// no one can modify it...you're stuck with Colt and Elie
// slightly different syntax
let addTo = function (passed) {
let add = function (inner) {
return passed + inner;
}
return add;
}
console.log(addTo(3)); // 5
let addThree = addTo(3);
let addOne = addTo(1);
console.log(addThree); // returns inner function
console.dir(addThree); // shows us that it is an object --- if look in `[[Scopes]]` and then
// look inside `Closure` you can see the `passed` variable, which
// means we can use it later
console.log(addThree(3)); // 6
console.log(addOne(2)); // 3
- NOTE: can play with this last bit of code in the repo:
learning-functional-javascript-with-ramda/basic_knowledge/closures