What is a practical use for a closure in JavaScript?
Suppose, you want to count the number of times user clicked a button on a webpage.
For this, you are triggering a function on onclick
event of button to update the count of the variable
<button onclick="updateClickCount()">click me</button>
Now there could be many approaches like:
-
You could use a global variable, and a function to increase the counter:
var counter = 0; function updateClickCount() { ++counter; // Do something with counter }
But, the pitfall is that any script on the page can change the counter, without calling
updateClickCount()
.
-
Now, you might be thinking of declaring the variable inside the function:
function updateClickCount() { var counter = 0; ++counter; // Do something with counter }
But, hey! Every time
updateClickCount()
function is called, the counter is set to 1 again.
-
Thinking about nested functions?
Nested functions have access to the scope "above" them.
In this example, the inner function
updateClickCount()
has access to the counter variable in the parent functioncountWrapper()
:function countWrapper() { var counter = 0; function updateClickCount() { ++counter; // Do something with counter } updateClickCount(); return counter; }
This could have solved the counter dilemma, if you could reach the
updateClickCount()
function from the outside and you also need to find a way to executecounter = 0
only once not everytime.
-
Closure to the rescue! (self-invoking function):
var updateClickCount = (function(){ var counter = 0; return function(){ ++counter; // Do something with counter } })();
The self-invoking function only runs once. It sets the
counter
to zero (0), and returns a function expression.This way
updateClickCount
becomes a function. The "wonderful" part is that it can access the counter in the parent scope.This is called a JavaScript closure. It makes it possible for a function to have "private" variables.
The
counter
is protected by the scope of the anonymous function, and can only be changed using the add function!
A more lively example on closures
<script>
var updateClickCount = (function(){
var counter = 0;
return function(){
++counter;
document.getElementById("spnCount").innerHTML = counter;
}
})();
</script>
<html>
<button onclick="updateClickCount()">click me</button>
<div> you've clicked
<span id="spnCount"> 0 </span> times!
</div>
</html>
Reference: JavaScript Closures
I've used closures to do things like:
a = (function () {
var privatefunction = function () {
alert('hello');
}
return {
publicfunction : function () {
privatefunction();
}
}
})();
As you can see there, a
is now an object, with a method publicfunction
( a.publicfunction()
) which calls privatefunction
, which only exists inside the closure. You can not call privatefunction
directly (i.e. a.privatefunction()
), just publicfunction()
.
It's a minimal example, but maybe you can see uses to it? We used this to enforce public/private methods.
The example you give is an excellent one. Closures are an abstraction mechanism that allow you to separate concerns very cleanly. Your example is a case of separating instrumentation (counting calls) from semantics (an error-reporting API). Other uses include:
-
Passing parameterised behaviour into an algorithm (classic higher-order programming):
function proximity_sort(arr, midpoint) { arr.sort(function(a, b) { a -= midpoint; b -= midpoint; return a*a - b*b; }); }
-
Simulating object oriented programming:
function counter() { var a = 0; return { inc: function() { ++a; }, dec: function() { --a; }, get: function() { return a; }, reset: function() { a = 0; } } }
Implementing exotic flow control, such as jQuery's Event handling and AJAX APIs.
JavaScript closures can be used to implement throttle and debounce functionality in your application.
Throttling
Throttling puts a limit on as a maximum number of times a function can be called over time. As in "execute this function at most once every 100 milliseconds."
Code:
const throttle = (func, limit) => {
let isThrottling
return function() {
const args = arguments
const context = this
if (!isThrottling) {
func.apply(context, args)
isThrottling = true
setTimeout(() => isThrottling = false, limit)
}
}
}
Debouncing
Debouncing puts a limit on a function not be called again until a certain amount of time has passed without it being called. As in "execute this function only if 100 milliseconds have passed without it being called."
Code:
const debounce = (func, delay) => {
let debouncing
return function() {
const context = this
const args = arguments
clearTimeout(debouncing)
debouncing = setTimeout(() => func.apply(context, args), delay)
}
}
As you can see closures helped in implementing two beautiful features which every web application should have to provide smooth UI experience functionality.