1. What is closures
In most books, closures are defined as "closures refer to functions that have access to variables in another function scope." ”。 The concept is too abstract to be helpful to beginners. Fortunately, the JavaScript Ninja Cheats 5.1 gives an example to further explain what a closure is:
var outervalue= ' ninja '; var later; function Outerfunction () { var innervalue = "Samurai"; function Innerfunction (paramvalue) { assert (Outervalue = = "Ninja", "I can see the Outervalue.") ; ASSERT (Innervalue = = "Samurai", "I can see the innervalue."); ASSERT (Paramvalue = = "Wakizashi", "I can see the paramvalue."); ASSERT (Toolater = = "Ronin", "Inner can see the Toolater.") ; later = innerfunction; } ASSERT (Toolater, "Outer can ' t see the Toolater."); var toolater = "Ronin"; Outerfunction (); Later ("Wakizashi");
The test results are:
Look, this later is pointing to a closure, which actually points to an intrinsic function innerfunction in an external function outerfunction. When the Outerfunction function is called through the global variable later the Innerfunction function is released from the closed prison of the Outerfunction function, the Innerfunction function suddenly becomes super powerful and becomes a closure, Once the closure is called, it can see both the global Outervalue, the prison innervalue, the paramvalue they carry with them, and the toolater they have never known before.
Of course, I think this example is not complete, in order to form the integrity, I give it to strengthen a bit:
Asserts (); Test ("function closure", functions () {var before_outerfunction = "Before_outerfunction"; function Outerfunction (outerparam) {var before_innerfunction = "Before_innerfunction"; function Innerfunction (innerparam) {return {before_outerfunction : Before_outerfunction, after_outerfunction:after_outerfunction, b Efore_innerfunction:before_innerfunction, After_innerfunction:after_innerfunction, Outerparam:outerparam, Innerparam:innerparam, Before_callclosure:before_callclosure, After_callclosure:after_callclosure, }; } var after_innerfunction = "After_innerfunction"; return innerfunction; } var after_outerfunction = "After_outerfunction"; var closure = outerfunction ("Outerparam"); var before_callclosure = "Before_callclosure"; var ret = closure ("Innerparam"); ASSERT (Ret.before_outerfunction, "before_outerfunction"); ASSERT (Ret.after_outerfunction, "after_outerfunction"); ASSERT (Ret.before_innerfunction, "before_innerfunction"); ASSERT (Ret.after_innerfunction, "after_innerfunction"); ASSERT (Ret.outerparam, "Outerparam"); ASSERT (Ret.innerparam, "Innerparam"); ASSERT (Ret.before_callclosure, "before_callclosure"); ASSERT (Ret.after_callclosure, "after_callclosure"); var after_callclosure = "After_callclosure"; });
The test results are:
The conclusion is that when the closure is called the moment, it immediately deeds, both can see see, also can see roost see, past life, all kinds, all vividly. Only the after_callclosure that have not yet happened, that is really invisible.
No wonder more than one book mentions that only understanding the closure can really understand JavaScript, which is a counter-intuitive anomaly.
2. Function Scope Chain
If you just know what a closure is, the paragraph above is enough, but it's far from being a closed-loop understanding. "JavaScript Ninja Cheats" is here and its irresponsible beginning to talk about how to use closures:
- Implementing private variables with closures
- Using closures in callback functions
- Using closures in timers
- Bind with a closure implementation function
- Curry of function with closure
- Caching of function results with closures
- Wrapping of functions with closures
The Ninja Master estimated that it was too much to drink, simply exposed his hands after throwing us a bunch of palm method, footwork, sword, knife. Only the most critical internal organs heart to forget. Well, do not expect it, or continue to find the master himself, money is wayward, please a bunch of teachers on the table, so it has the emboldened. So I saw the "JavaScript advanced Programming", this awesome master. Master is a master, a come up to tell me, to understand the closure, first turn to the fourth chapter to see what is the scope chain. Look over there. 4.2 About the execution environment and scope, only a short two pages, the general meaning is: through the implementation of the environment, scope chain, activity objects, we implemented a layer of variables to find. Come on, my IQ is not enough, continue to change teacher, so I found the "high-performance JavaScript", the second chapter of a small section, the title is "scope chain and identifier Resolution", is also a short two pages, Abas, illustrated, immediately a kind of clairvoyant feeling.
Each JavaScript function is represented as an object, or, more specifically, an instance of a function object.
When the compiler sees the following global function:
function Add (NUM1, num2) { var sum = num1 + num2; return sum; }
It creates a function object by using code similar to the following:
var add = new Function ("Num1", "num2", "var sum = num1 + Num2;\nreturn sum;");
The construction of the scope chain to be completed automatically in function functions:
- Create a scope chain object for the Add function object and save the reference in the [Scope] property of the Add Function object
- Point the first item of the scope chain object to the global scope object
It sounds complicated, visualize it with the following diagram:
When add is called, for example, by the following code:
var total = Add (5, 10);
It's time for the engine to work, and it's going to do the following:
- Executing this function creates an internal object called the execution Environment (execution context). An execution environment defines the environment at which a function executes. The execution environment for each execution of the function is temporary, so calling the same function multiple times will result in multiple execution environments being created. When the function is executed, the execution environment is destroyed.
- Each execution environment has its own scope chain, which is used to resolve identifiers. When the execution environment is created, its scope chain is initialized to the object in the [Scope] property of the currently running function. These values are copied into the scope chain of the execution environment according to the order in which they appear in the function.
- Once this process is complete, a new object called the "Activation Object" is created for the execution environment. The active object acts as a variable object when the function is run, including all local variables of the function, named arguments, parameter sets, and this.
- The active object is then pushed into the forefront of the execution environment scope chain.
- Each time the identifier is parsed during the execution of the function, the previous lookup is in the scope chain of the execution environment
- After the function is executed, the execution environment is destroyed and the active object is destroyed.
And then visualize it with the pictures on the book:
3. Closure and function scope chain
is still high-performance JavaScript, chapter Two, a small section titled "Closures, scopes, and memory." The sample code given is as follows:
function savedocument (ID) { } function assignevents () { var id = "xdi9592"; document.getElementById ("Save-btn"). onclick = function (event) { savedocument (ID); } }
The assignevents () function sets the event handler function for a DOM element. This event handler is a closure that is created when assignevents () executes and can access the ID variable of the owning scope. In order for this closure to access the ID, a specific scope chain must be created.
When the assignevents () function executes, an active object containing the variable ID and other data is created. It becomes the first object in the execution environment scope chain, and the global object is immediately followed. When a closure is created, its [Scope] property is initialized to these objects.
Because the closure's [Scope] property contains a reference to the same object as the execution environment scope chain, it can have side effects. Typically, the active object of a function is destroyed along with the execution environment. However, when a closure is introduced, the activation object cannot be destroyed because the reference still exists in the [Scope] property of the closure.
When a closure is executed, an execution environment is created, and its scope chain is initialized with the two same scope chain objects referenced in the properties [[Scope]], and an active object is created for the closure itself.
JavaScript advanced programming also has a similar example, as shown in the example code:
function Createcomparisonfunction (PropertyName) { return function (Object1, object2) { var value1 = object1[ PropertyName]; var value2 = Object2[propertyname]; if (value1 < value2) { return-1; } else if (value1 > value2) { return 1; } else { return 0;
} }; } Create function var comparenames = createcomparisonfunction ("name"); Call function var result = Comparenames ({name: "Nicholas"}, {name: "Greg"}); Dereference a reference to an anonymous function (to free memory) comparenames = null;
Function Comparenames is invoked when the execution environment is as follows:
This picture is very easy to cause misunderstanding, many people think this picture is a function comparenames is called when a snapshot, in fact, when the Createcomparisonfunction function is executed, The execution environment of the Createcomparisonfunction function is correct. But the anonymous function it returns is a closure, so the scope chain of the anonymous function replicates the scope chain of the Createcomparisonfunction function's execution environment, and then the Createcomparisonfunction function ends, The execution environment of the Createcomparisonfunction function is destroyed, but the active object of the Createcomparisonfunction function cannot be destroyed because it is referenced by the closure.
When Comparenames executes, it still follows the execution flow of the normal function:
- Create the execution environment of the function;
- Create a scope chain for the execution environment of the function, and copy the function's scope chain;
- Creates the active object of the function and joins the first bar of the scope chain to the function's execution environment
4. What is a global object?
This question does not seem to mention the level of Ah, learning JavaScript beginners who do not know the importance of global objects. But if you look at it another way, if you think of all the JavaScript code as written in one of the outermost super functions. So when this super function executes, it should also continue the kick of the function call:
- Create the execution environment for the Super function;
- Creates a scope chain for the execution environment of the super function and copies the scope chain of the superscope, which is empty at this time;
- Create a super function for the active object and add the first bar of the scope chain to the execution environment of the Super function
Based on the retrieval mechanism of the function scope chain and the use of global objects, we seem to have a corollary: The global object is actually the active object of the Super function.
Further, all of the global functions we define are actually closures, as they all copy the Super function's active object, which is the global function, into its own function scope chain.
Back to our first test case for closures, if we do not return intrinsic functions directly, but instead directly invoke intrinsic functions in external functions?
Test ("function closure", functions () {var before_outerfunction = "Before_outerfunction"; function Outerfunction (outerparam) {var before_innerfunction = "Before_innerfunction"; function Innerfunction (innerparam) {return {before_outerfunction : Before_outerfunction, after_outerfunction:after_outerfunction, b Efore_innerfunction:before_innerfunction, After_innerfunction:after_innerfunction, Outerparam:outerparam, Innerparam:innerparam, Before_callclosure:before_callclosure, After_callclosure:after_callclosure, }; } var after_innerfunction = "After_innerfunction"; var ret = innerfunction ("xxx"); ASSERT (Ret.before_outerfunction, "before_outerfunction"); ASSERT (Ret.after_outerfunction, "after_outerfunction"); ASSERT (Ret.before_innerfunction, "before_innerfunction"); ASSERT (Ret.after_innerfunction, "after_innerfunction"); ASSERT (Ret.outerparam, "Outerparam"); ASSERT (Ret.innerparam, "Innerparam"); ASSERT (Ret.before_callclosure, "before_callclosure"); ASSERT (Ret.after_callclosure, "after_callclosure"); return innerfunction; } var after_outerfunction = "After_outerfunction"; var closure = outerfunction ("Outerparam"); Log (closure); var before_callclosure = "Before_callclosure"; var ret = closure ("Innerparam"); ASSERT (Ret.before_outerfunction, "before_outerfunction"); AssertRet.after_outerfunction, "after_outerfunction"); ASSERT (Ret.before_innerfunction, "before_innerfunction"); ASSERT (Ret.after_innerfunction, "after_innerfunction"); ASSERT (Ret.outerparam, "Outerparam"); ASSERT (Ret.innerparam, "Innerparam"); ASSERT (Ret.before_callclosure, "before_callclosure"); ASSERT (Ret.after_callclosure, "after_callclosure"); var after_callclosure = "After_callclosure"; });
The test results are:
As expected, the so-called closure is not a return, but rather the moment the intrinsic function is created by the compiler to create the function object. Copies the scope chain of the current function execution environment for the scope chain of the function. By visualizing:
The key is that this set of function definitions, function call mechanism can be nested indefinitely, and function definition and function call time is also separate. Each function executes with its own active object responsible for managing its own scope, and the responsibility for performing an environmental scope chain is simply to concatenate the respective active objects of these nested functions. The function's scope chain is simply the equivalent of an intermediate variable, which is responsible for preserving the scope chain of the upper-level function execution environment.
So an intrinsic function that is called directly within an external function can also access the related variables of the intrinsic function, not because the external function is actually visible when the internal function is called, but because the scope chain of the external function's execution environment has been replicated in the scope chain of the inner function's execution environment. The scope chain of a function's execution environment is self-sufficient, and function calls are only found in the scope chain of the execution environment of their function, in fact it does not know anything about external functions or global functions.
If the internal function does not return, everything will be done with the external function call, the external function execution environment object is destroyed, resulting in the execution of external functions of the scope chain is destroyed, resulting in the external function of the active object is destroyed, while the internal function object as a local variable will be destroyed. This series of destruction processes copies the scope chain of an internal function to replicate the scope chain of the external function's execution environment, and the "evidence" is masked seamlessly. The active object of the temporary external function will never go outside the cage.
But once the internal function is sent out, the scope chain of the inner function replicates the scope chain of the external function's execution environment, the "incriminating" is exposed, and the active object of the external function that was supposed to be destroyed is unexpectedly alive, waiting to continue as the inner function is called. In fact, not only the external function of the active object, the external function of the execution environment of the scope chain of all the active objects are unexpectedly alive, if we construct a two layer above the function of nesting, continuous function definition and function calls, and finally return to a most inner layer of the function, You will find that a group of objects that should have died unexpectedly survived.
is setting null directly after a closure is performed to ensure that the active objects that are accidentally survived are cleared? This is supposed to be the case, but it's very uncertain, depending on how the engine's garbage collection works, and this inference should be true if you follow the GC of reference counts, but most engines now use the garbage collection method of stamp removal, which makes it hard to guarantee that this inference is true. Or, more simply, with the automatic removal of a closure reference variable, it's also reasonable to let those active objects die. This may also explain that few of the JavaScript code you see are actively set to null for closures.
In JavaScript's world, its design thought is still pure and simple.
How do I manage functions? JavaScript replies with function objects.
How do I manage the scope of a function? JavaScript responds with the active object.
What if a function call is nested? JavaScript responds by using a chain of scopes to string the active objects together.
What if an external function returns an intrinsic function that causes the external function's active object to leak? JavaScript answers that it's called closures.
5. PostScript
All the credit of this article belongs to the authors of the classic books, I do not produce knowledge, I am only the porter of knowledge. All the mistakes in this article belong to me personally, who let me be a beginner, sometimes move the wrong is inevitable, it is constantly wrong constantly to correct constantly grow it.
JavaScript Learning-Closures