Closures in javascript
Lets start with an analogy.
A closure lets a function remember and access its birth-scope, even when it’s running elsewhere.
A closure is basically a function that has access to the variables declared in the scope of the outer function, even after the outer function has returned / finished executing.
Lexical Scope / environment
When a function is defined inside another function, the inner function has access to the variables declared in the outer function. This is called lexical scope.
Lets see an example.
In this example, when innerFunction executes, its lexical environment includes:
- Its own scope (containing innerVar).
- The scope of outerFunction (containing outerVar).
- The global scope (containing globalVar).
Saving state
The real power of closures comes into play when the inner function is returned from the outer function. Even after the outer function has completed its execution and its local variables should theoretically be gone, the inner function remembers and retains access to the outer function's variables (its lexical environment).
When to use closures
-
You can use closures to create private variables in JavaScript. By encapsulating variables within a closure, you can prevent them from being accessed or modified from outside the closure.
-
Closures are useful for creating functions that maintain state. If you noticed, in counter example, the last count value is retained even after the function has returned.
-
Closures are commonly used in event handlers and callbacks to maintain access to variables from their surrounding scope when the event or asynchronous operation occurs.
Issues with closures
-
Memory Leaks: Closures can lead to memory leaks if not managed properly. If a closure holds references to large objects or functions, it can prevent those objects from being garbage collected, even if they are no longer needed.
-
Performance: Closures can have a slight performance impact due to the additional overhead of managing closures. However, in most cases, this impact is negligible and can be optimized by using techniques like function caching or lazy initialization.
Implementing throttle function with closure
A throttle function is a utility function that limits the rate at which a given function can be called.
It takes a function and a time interval as arguments. The function is executed at most once in every time interval.
Since we have to write a function which controls the rate of execution of other function, lets start defining our throttle function first. This function will return the throttled function
Now since we have to execute the function after a certain time interval, we can use setTimeout function.
Try the code in the sandbox below.
Implementing debounce function with closure
Debouncing is a technicque where you basically wait for inactivity before executing a function. So you wait for the user to finish the input before executing the function. For example, in a search bar, you want to wait for the user to finish typing before executing the search api call.
Practical Use Cases for Debounce:
- Search Autocomplete: As mentioned, waiting for the user to stop typing before fetching suggestions reduces unnecessary API calls and improves performance.
- Resizing Events: When a user resizes a window, the resize event fires rapidly. Debouncing ensures that expensive layout recalculations or redraws only happen once after the user has finished resizing.
- Scroll Events (less common): In some cases, you might want to trigger an action only after the user has stopped scrolling for a bit (though throttling is often a better fit here).
Implementing memoize function with closure
Memoization is a technique used to optimize the performance of functions by caching the results of expensive function calls and returning the cached result when the same inputs occur again.