Closure is an important feature in JavaScript. Its biggest role is to save information during the running of the function. In JavaScript, many features of closures originate from the scope chain during function calls.
Scope chain of function call objects and variables
For each function call in JavaScript, JavaScript will create a local object to store the local variables defined in the function; if there is a nested function defined inside the function, then JavaScript will Define a nested local object on top of an already defined local object. For a function, there are as many layers of nested local objects as there are layers of nested function definitions inside it. This local object is called a "function call object" ("call object" in ECMAScript 3, renamed "declarative environment record" in ECMAScript 5, but personally I think the name in ECMAScript 3 is easier to understand). Take the following function call as an example:
In this simple example, when the f() function is called, JavaScript will create a call object of the f() function (let’s call it f_invokeObj). There are two attributes inside the f_invokeObj object: a and x; When running f(), the value of a is 10 and the value of x is 6, so the final return result is 60. The icon is as follows:
When function nesting exists, JavaScript will create multiple function call objects:
In this example, when the f() function is called, JavaScript will create a call object (f_invokeObj) of the f() function, which has two internal attributes a and x. The value of a is 10 and the value of x is 6; run f (), JavaScript will parse and define the g() function in the f() function, and create the calling object (g_invokeObj) of g(), which has an internal attribute b, and the b value is the same as the incoming parameter x, which is 6 , so the final return result is 360. The icon is as follows:
As you can see, the function call objects form a chain. When the embedded function g() is running and needs to obtain the variable value, the search will start from the nearest function call object. If it cannot be searched, it will search in further call objects along the function call object chain. This is the so-called "variable scope chain". If the same variable appears in two function call objects, the function will take the variable value in the call object closest to itself:
In the above example, variable a exists in both the calling object of the g() function (g_invokeObj) and the calling object of the f() function (f_invokeObj) and the value of a is different. When the g() function is run, in g() The value of a used inside the function is 1, while the value of a used outside the g() function is 10. The function call object chain shown in the figure is as follows:
What is a closure?
All functions (functions) in JavaScript are objects, and when a function is defined, a corresponding function call object chain will be generated. A function definition corresponds to a function call object chain. As long as the function object exists, the corresponding function call object exists; once a function is no longer used, the corresponding function call object will be garbage collected; and this one-to-one combination between the function object and the function call object chain, Just call it "closure". In the above example of the f() function and the g() function, there are two closures: the f() function object and the f_invokeObj object form a closure, and the g() function object and the g_invokeObj-f_invokeObj object chain form together The second closure. When the g() function finishes executing, the g() closure is garbage collected because the g() function is no longer used; later, when the f() function finishes executing, f() is closed for the same reason. The package was also garbage collected.
From the definition of closure, we can conclude that all JavaScript functions are closures after definition - because all functions are objects, all functions also have their corresponding calling object chain after execution.
However, where closures really come into their own is in the case of nested functions. Since the inline function is defined when the external function is running, the variable values saved in the closure of the inline function (especially the local variable values of the external function) are the values during this run. As long as the embedded function object still exists, its closure will still exist (the variable values in the closure will not change), thus achieving the purpose of saving information about the function's running process. Consider this example:
In this example, when the f() function is run, the g() function is defined and a closure of the g() function is created. The g() closure contains the g_invokeObj-f_invokeObj object chain, thus saving f() The value of variable a during function execution. When the console.log() statement is executed, since the g function object still exists, the g() closure still exists; when running the g function object that still exists, JavaScript will use the g() closure that still exists and Get the value of variable a ("inside") from it.