When I was learning about closures in JavaScript for the first time I saw a lot of examples like this:
const counter = (function() {
let count = 0
return function() {
count++
return count
}
})()
console.log(counter()) //1
console.log(counter()) //2
console.log(counter()) //3
Now, it was pretty clear to me what was happening; the outer function (which is an Immediately Invoked Function Expression or IIFE for short) declares the count
variable, assigns the number zero to it and then returns the inner function. This inner function is executed each time we call count()
and 'knows' about the count
variable because it has closure over it.
So all well and good. The bit that was not clear to me was why this would be useful! As such, I would like to demonstrate a couple of ways in which I use closures in my daily coding.
Example one
The first technique is encapsulation. This is a way of creating a module where the inner workings are private and only certain methods are exposed for other modules to interact with them. It is exactly what is happening in the example above but it is (hopefully!) easier to see why it is useful when we look at some code which has a bit more going on.
const library = (function() {
const books = ['Island', '1984', 'Dracula', 'Papillon']
return {
getBooks() {
return [...books]
},
deleteBook(title) {
books.splice(books.indexOf(title), 1)
},
addBook(title) {
books.push(title)
},
}
})()
library.addBook('catch 22')
library.deleteBook('Dracula')
console.log(library.getBooks()) // [Island, 1984, Papillon, catch 22]
const libraryBooks = library.getBooks()
libraryBooks.push('Beowulf')
console.log(libraryBooks) // [Island, 1984, Papillon, catch 22, Beowulf]
console.log(library.getBooks()) // [Island, 1984, Papillon, catch 22]
As you can see, our books
array is protected from the outside world by the inner function's closure over it. The only way to interact with it is through the public methods; getBooks()
returns a copy of the array instead of a reference to it and books can only be added or deleted one at a time.
Example two
The second example is a pattern I use frequently. I will use a React component to demonstrate, don't worry if you don't know React, it is pretty easy to see what is happening.
class StartEndDate extends React.Component {
state = {
start: null,
end: null
}
// This is our closure, I have used es6 arrow functions this time
updateTime = startOrEnd => newTime => (
this.setState({
[startOrEnd]: newTime
})
)
render() {
return (
<DateInput label="From" updateTime={this.updateTime('start')} />
<DateInput label="To" updateTime={this.updateTime('end')} />
)
}
}
function DateInput({ label, updateTime }) {
return (
<label for="timeInput">From</label>
<input id="timeInput" type="time" onChange={updateTime}>
)
}
In this code we have one stateful React component, which is declared as an es6 class and one functional component. You should be able to see where the functional component is used, it is in the return of the render
method (the code that looks like HTML is actually JSX; it calls the DateInput
function, passing the props label
and updateTime
to it).
The DateInput
function returns the markup for a <label>
and <input>
. When a change event is detected, the value is passed through to the updateTime
method on the StartEndDate
class.
If you look at the render
method where this function is passed to DateInput
, it is first called with either the value "start" or "end". Our two inputs now have their own version of the updateTime
method which has closure over the startOrEnd
variable. Using computed property keys we can dynamically assign the new value to the component's state using the variable in closure.
These are two of my most often used closure patterns. I hope that I have been able to show why they are such a useful feature of the language and an important tool to have in your tool-set.
Top comments (0)