Http://blog.belltoy.net/javascript_scope_call_object_closure.html
Yesterday, someone asked me what the Javascript closure is. I found that I couldn't explain it clearly in just a few words. I just wrote it. In fact, the relationship between JavaScript function scopes, calling objects, and closures is very subtle.ArticleThere are already many, but I don't know why many new users are hard to understand. I will try to express my understanding in a more popular language.
Scope
Functions in Javascript belong to the lexical scope, that is, they run in the scope when they are defined, rather than in the scope when they are executed. This is what the rhino book says. But some people are confused about the "Definition" and "execution (called. To put it simply, when function a is "defined", function a () {} is executed when the function is defined, and function a is called () this statement is executed. These two concepts must be clearly divided.
What is the lexical scope (hereinafter referred to as "scope" unless otherwise specified? It is an abstract concept. To put it bluntly, it is a "range", and scope is the meaning of scope in English. The scope of a function is the "range" of the function when it is defined, that is, the "range" of its outer layer. This "range" contains the variable attributes of the outer layer, this "range" is set to an internal state of this function. When a global function is defined, the "range" of the global function (the outer layer of the function) is set to an internal state of the global function. When a nested function is defined, the "range" of the nested function (outer function) is set to an internal state of the nested function. This "internal status" can be understood as the scope chain. See the following.
When a function is called, in this function, it can access its internal state, it can also access all variables on the entire scope chain, of course, it also includes external variables. (Actually accessed from the "Call object chain. It seems that there is a problem. Please read it later .)
As mentioned above, the scope of a function is the "range" when it is defined, so the function scope in Javascript is determined when the function is defined, therefore, it is a static scope, and the lexical scope is also called a static scope.
Call object
The call object of a function is dynamic. It is instantiated only when the function is called. We already know that when a function is defined, its scope chain has been determined. When the javascript interpreter calls a function, it adds a new object (called object) to the front of the scope chain. An attribute of the called object is initialized to an attribute named arguments, which references the arguments object of the function. The arguments object is the actual parameter of the function. All local variables declared using the VaR statement are also defined in this call object. At this time, the called object is in the header of the scope chain. The local variables, function form parameters, and arguments objects are all in the scope of this function. Of course, local variables, function form parameters, and arguments objects overwrite attributes with the same name in the scope chain.
Relationships between scopes, scope chains, and calling objects
In my understanding, the scope is abstract, and the called object is instantiated.
When a function is defined, it is actually executed by its outer function. The scope chain it determines is actually the call object chain of its outer function. When a function is called, its scope chain is based on the defined scope chain (the call object chain of its outer function) plus an instantiated call object. Therefore, the function scope chain is actually the call object chain. When a function is called, its scope chain (or call object chain) is actually a superset of the scope chain it determines when it is defined.
The relationship between them can be expressed:The scope of the intent scope chain. The intent call object..
This is too difficult. For example:
123456 |
Function F ( X ) { VaR G = Function ( ) { Return X; } Return G; } VaR G1 = F ( 1 ) ; Alert ( G1 ( ) ) ; // Output 1 |
Suppose we regard the global function as a big anonymous function like the following:
123 |
(Function() {// Global scope})(); |
The example is as follows:
12345678 |
( Function ( ) { Function F ( X ) { VaR G = Function ( ) { Return X; } Return G; } VaR G1 = F ( 1 ) ; Alert ( G1 ( ) ) ; // Output 1 } ) ( ) ; |
- When a global big anonymous function is defined, it has no outer layer, so its scope chain is empty.
- The global large anonymous function is directly executed, and the global scope chain has only one'Global Call object'.
- Function f is defined. At this time, function F's scope chain is its outer scope chain, that is'Global Call object'.
- Function f (1) is executed, and its scope chain is the new F (1) Call object plus the scope chain when function f is defined, that is'F (1) Call object-> Global Call object'.
- Function g is defined in F (1) and its scope chain is its outer function f (1) scope chain, that is'F (1) Call object-> Global Call object'.
- Function f (1) returns the definition of function g to G1.
- Function G1 is executed, and its scope chain is the scope chain of the new G (1) Call object plus the outer F (1), that is'G1 call object-> F (1) Call object-> Global Call object'.
You can see it clearly.
Closure closuer
A simple saying of closures is that when nested functions are called outside of nested functions, they form a closure.
The previous example is actually a closure. G1 is defined in F (1), but is executed only after F (1) is returned. It can be seen that one of the effects of closures is that after being returned by nested function f, its internal resources will not be released. When calling the G Function externally, G can access the internal variables of F. Based on this feature, you can write a lot of elegantCode.
For example, if you want to make a uniform counter on a page, you can write it like this if you use the closure statement:
1234567891011 |
VaR Counter = ( Function ( ) { VaR I = 0; VaR FNS = { "Get" : Function ( ) { Return I; } , "Inc" : Function ( ) { Return ++ I; } } ;Return FNS; } ) ( ) ; // Do something Counter. INC ( ) ; // Do something else Counter. INC ( ) ; VaR C_value= Counter. Get ( ) ; // Now c_value is 2 |
In this way, a variable I is maintained in the memory, and the entireProgramThe value of I cannot be operated directly in other places. Only two counter operations can be performed.
In setTimeout (FN, delay), we cannot pass parameters to the FN function handle, but we can bind the required parameters to the FN through the closure method.
12345 |
For ( VaR I = 0 , Delay= 1000 ; I < 5 ; I ++, Delay + = 1000 ) { SetTimeout ( Function ( ) { Console. Log ( 'I :' + I + "Delay :" + Delay ) ; } , Delay ) ; } |
In this way, all the printed values are
I:5Delay:6000I:5Delay:6000I:5Delay:6000I:5Delay:6000I:5Delay:6000
Using the closure method, you can easily bind the parameters to be passed in:
1234567 |
For ( VaR I = 0 , Delay = 1000 ; I < 5 ; I ++, Delay + = 1000 ) { ( Function ( A , _ Delay ) { SetTimeout ( Function ( ) { Console. Log ( 'I :' + A + "Delay :" + _ Delay ) ; } , _ Delay ) ; } ) ( I , Delay ) ; } |
Output:
I:0 Delay:1000I:1Delay:2000I:2Delay:3000I:3Delay:4000I:4Delay:5000
Closure also has a very common feature, that is, when binding Event Callback functions. Similarly, the bound function handle cannot be used as a parameter, but can be used as a closure to bind the parameter.
Summary
- function lexical scopes and scope chains are different. Lexical scopes are abstract concepts, and scopes are instantiated call object chains.
- when a function is defined and its outer function is executed.
- when a function is defined, its lexical scope is fixed, but it is still an abstract concept and cannot be instantiated.
- when a function is defined, it also determines a thing, that is, the scope chain of its outer function, which is instantiated.
- when a function is called multiple times, its scope chain is different.
- the closure is powerful. The rhino book is right. After understanding these things, you can claim to be a senior JavaScript programmer. By taking advantage of these concepts, you can play with many JavaScript design patterns.