This tutorial is a part of the Learn everything about Javascript in one course.
What is closure?
A closure
(a.k.a lexical closure or function closure), is a technique for implementing lexically scoped name binding in a language with first-class functions. Closure
is a property of function and other programming languages' function also have it.
In simple language, closure:
- Is created whenever a function (
inner function
) created inside another function (outer function
). - All variable defined at
outer function
will beremembered
and can beused
byinner function
.
// What is closure?
function createCounter() { // outer function
let count = 0 // closure data, is "remember" and "can be used" by inner function
return { // an object with 3 methods is returned
increase: () => count = count + 1, // inner function
decrease: () => count = count - 1, // inner function
print: () => console.log(count), // inner function
}
}
const counter = createCounter()
counter.print() // 0
counter.increase()
counter.print() // 1
counter.decrease()
counter.decrease()
counter.print() // -1
What is closure used for?
Closure
is good to use whenever you have a function when running, it depends on:
- Shared data between runs.
- Shared data is encapsulated in one single function.
In general, closure
gives you shared data between different runs of a function . The case of usage is pretty much depends on how you creatively use it.
setTimeout()to run a function after specified time
Syntax setTimeout(functionToRun, timeMiliseconds)
. setTimeout()
is a a built-in, global function that you can call anywhere in your program.
// setTimeout()to run a function after specified time
const printAfter500ms = () => {
console.log('this message is printed after 500 ms')
}
setTimeout(printAfter500ms, 500)
setTimeout(() => console.log('this message is printed after 100 ms'), 100)
// this message is printed after 100 ms
// this message is printed after 500 ms
Javascript closure example 1: delay calling a function
Problem:
- Assume you were a Goolge software engineer and tasked to write a function that return search suggestion after user type in.
- Search suggestion is the list of suggestions on Google Search Bar after each of your keystroke. Your task is to write a function called
getSuggestion()
. - Assume that we are at the early days of the internet and Google servers are limited. So your
getSuggestion()
only makes a request to the server 1 second after the last keystroke.
Analysis:
getSuggestion()
needs to remember to go fetch suggestion 1 second after 1st keystroke.getSuggestion()
needs to remember the last keyword.getSuggestion()
needs to remember if there is any pending fetch.- Data is shared between different runs -> using closure is a good way to solve this problem.
// Javascript closure example: delay calling a function
const TOP_BOTTOM = 'TOP_BOTTOM'
const LABEL = 'FIRST_CALL'
console.time(TOP_BOTTOM) // timer TOP_BOTTOM start
console.time(LABEL) // timer FIRST_CALL start
const createGetSuggestion = () => { // outer function
let ok = false // closure data, true for go fetch suggestion data
let pending = false // closure data
let keyword = '' // closure data
const get = (word) => { // inner function
keyword = word
if(ok) {
console.log(`now get suggestions with keyword '${keyword}'`) // make call to server and get suggestion
console.timeEnd(LABEL); // timer FIRST_CALL end
pending = false
ok = false
} else {
console.log('please wait')
if(!pending) {
pending = true
setTimeout(() => {
ok = true
get(keyword)
}, 1000); // timeout in 1 second
}
}
}
return get // inner function returned
}
const getSuggestion = createGetSuggestion()
// each keystroke will call "getSuggestion()" once
getSuggestion('i') // please wait (1st keystroke)
getSuggestion('ir') // please wait (2nd keystroke)
getSuggestion('iro') // please wait (3rd keystroke)
getSuggestion('iron') // please wait (4th keystroke)
getSuggestion('iron ') // please wait (5th keystroke)
getSuggestion('iron m') // please wait (6th keystroke)
console.timeEnd(TOP_BOTTOM); // TOP_BOTTOM: 6.192ms
// now get suggestions with keyword 'iron m'
// FIRST_CALL: 1008.117ms
Javascript closure example 2: memoization
Memoization
or memoisation is an optimization technique used primarily to speed up computer programs by storing the results of expensive function calls and returning the cached result when the same inputs occur again.
Problem:
- Assume that getting UTF16 code of a character is a very expensive computing problem.
- Write a function call
getCodeAndCache()
that do "heavy" UTF16 look up for never "seen" input. getCodeAndCache()
should cache result of "seen" input and return cached result if see the same input again.
Analysis:
getCodeAndCache()
needs to remember input & output.- Data is shared between different runs -> using closure is a good way to solve this problem.
// Javascript closure example: memoization
const createGetCodeAndCache = () => { // outer function
const cache = {} // closure data
const get = (input) => { // inner function
console.log(cache) // print cache before each run
if(cache[input]) { // use cache if available
console.log('cached output')
return cache[input]
} else { // compute if no cache available
console.log('new input')
const result = input.charCodeAt(0)
cache[input] = result // put data to cache
return result
}
}
return get
}
const getCodeAndCache = createGetCodeAndCache()
console.log(getCodeAndCache('a'))
// {}, empty cache
// new input
// 97
console.log(getCodeAndCache('b'))
// { a: 97 }, cached 'a'
// new input
// 98
console.log(getCodeAndCache('a')) // "seen" input
// { a: 97, b: 98 }, cached 'a' and 'b'
// cached output
// 97
Summary
Closure
function has shared data between different runs.- Use of deeply nested closure should be
avoided
as it consumes a lot of resources.