JavaScript Closures

August 3, 2020

The closure is at the base of a function that remembers the variables from the place where it was defined, regardless of where it will be executed later. In other words, a closure is a function bundled with its lexical scope. Callbacks, event handlers, higher-order functions can access variables from the outer scope thanks to that. The closure is created at runtime during the function creation. The JavaScript engine needs to keep the variable from the outer function available until the inner function has been called. In some cases, closures can be referred to as a "nested function" or "ghost/memory of the past function calls".

Function from the inner scope has access to variables defined in the outer scope because of the lexical (static) scoping. Even function executed outside of its lexical scope can access the variables defined in its lexical scope.

How can closure be easily recognized? If a function uses a variable that is not defined inside the function scope, it is probably a closure.

The inner function gets access to the parameters and variables of the outer function except this and arguments.

1const statusObject = (function () {
2 const objectStatus = 'success'
3
4 return {
5 getObjectStatus: function () {
6 return objectStatus
7 },
8 }
9})()
10
11objectStatus // ReferenceError: objectStatus is not defined
12statusObject.objectStatus // undefined
13statusObject.getObjectStatus() // => 'success'

Instead of initializing statusObject with an object literal, we can initialize statusObject by calling a function that returns an object. That function defines a objectStatus variable. That variable is always available to the getObjectStatus method, but the function scope keeps it hidden from the rest of the code. We are assigning the result of the function to statusObject, and we can use the power of IIFE (Immediately invoked function expression) to invoke this function immediately. The function returns an object containing a getObjectStatus, and this method has the privilege of access to the objectStatus constant.

1const statusObject = function (objectStatus) {
2 return {
3 getObjectStatus: function () {
4 return objectStatus
5 },
6 }
7}
8
9const myObject = statusObject('warning')
10
11objectStatus // ReferenceError: objectStatus is not defined
12myObject.objectStatus // undefined
13myObject.getObjectStatus() // => 'warning'

When we call statusObject with objectStatus argument, the function returns a new object containing a getObjectStatus method. A reference to a newly created object is stored in the myObject constant. The getObjectStatus method still has privileged access to the objectStatus argument, event. However, statusObject has already been returned. getObjectStatus has access to the parameter itself, not to a copy of the parameter. Access to the parameters is possible because the function has access to the context in which it was created. It is essential to understand that the inner function can access the outer function's actual variables and not copies.