JavaScript Closures
August 3, 2020The 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
.
const statusObject = (function () {const objectStatus = 'success'return {getObjectStatus: function () {return objectStatus},}})()objectStatus // ReferenceError: objectStatus is not definedstatusObject.objectStatus // undefinedstatusObject.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.
const statusObject = function (objectStatus) {return {getObjectStatus: function () {return objectStatus},}}const myObject = statusObject('warning')objectStatus // ReferenceError: objectStatus is not definedmyObject.objectStatus // undefinedmyObject.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.