Change record:
November 27, 19, modified!
In the last section we talked about execution context, so in this section we’re going to go after closures! What makes your headache go away!
Noun explanation:
The variable object
Variable objects can also be translated into Variable objects, which are objects that hold variables, active objects, and closure objects.
Note: here to explain, because of the use of these nouns in various books, do very far.
Execution context
The Execution Context is an Execution Context. When a function executes, the execution context is first created to run the code.
Active objects
An Activation Object can also be translated as an activated Object. At function execution, a variable object is created that not only contains variables, but also the special this, arguments.
closure
It’s a closed environment. It’s a closed environment. Enclose the environment outside the current scope for use by other scope environments.
Summary: A lot of nouns are translated from English, read the original work or according to the context to understand the real meaning of these words, only can understand can not explain…
When a function creates an argument, it declares the variable internally
function f(arg){ console.log(arg) } f(); //undefined function f(arg){ arg = 5; console.log(arg); } f(); / / 5Copy the code
2. Parameter passing is equivalent to variable copy (value passing)
For primitive types, variables hold data. For reference types, variables hold memory addresses. Parameter passing is copying the stored value of a variable to a parameter.
var o = { a: 5 }; function f(arg){ arg.a = 6; } f(o); console.log(o.a); / / 6Copy the code
Garbage collection mechanism
JavaScript has automatic garbage collection, and the execution environment is responsible for managing the memory used during code execution. In a function, normal local variables and function declarations exist only during the execution of the function, and when the function is finished, they are freed (destroying variables and functions).
There are two main collection methods in JS:
- Tag cleanup (common) // Give variables labeled “in” and “out of” and reclaim variables labeled “out of”.
- Reference count // If a reference type value is assigned to a variable, the number of references is increased by 1. If a reference type value is obtained from the variable, the number of references is decreased by 1.
Know a general situation can be, “JavaScript advanced programming third edition” section 4.3 has a detailed explanation, interested, can see. .
4. Scope
In JavaScript, a scope is literally the scope (accessible scope) of variables and functions.
-
A scope can be entity as a Variable Object.
-
The scopes in JavaScript are global and local (function scope). ES6 added block-level scopes
Global Scope
(1) Variables that are not defined in any function have global scope. (In non-strict mode)
(2) In fact, JavaScript has a global object window by default, and globally-scoped variables are actually bound to one of the window’s properties.
Local Scope
(1) JavaScript scopes are defined by functions. Variables defined in a function are only visible inside the function, called function (local) scopes.
Block-level scope block-level scope defines variables in blocks of If statements, switch statements, loop statements, etc. This means that variables cannot be accessed outside the block.
The relation between function and scope chain
Each function has a [[scope]] internal property (which can be viewed through console.dir(fn)) that holds a chain of scope objects (an array of objects). Function, etc.). When a function is created, a mutable object representing the global environment is inserted at the first location of the scope. This global mutable object holds window, Navigator, Document, and so on.
Declare a global function as follows:
function add(num1, num2) {
return num1 + num2
}
Copy the code
When a function executes, the execution context is created, followed by an execution context object that has its own chain of actions. At first, it initializes itself (that is, copies) with the scope chain in the function’s own [[scope]].
An active object is then created (also known as a variable object, variable object) that holds the variables, arguments, this, and so on in the current function scope. Finally, the live object is pushed to the top of the scope chain of the execution context.
Note: The execution context is destroyed after the function completes execution, along with its scope chain, variables, functions, active objects, this, and so on.
Scope chain lookup
During function execution, for each variable encountered, an identifier resolution process is performed to determine where to fetch the storage data. The procedure searches the scope chain of the execution environment for identifiers of the same name. The search process starts at the head of the scope chain, which is the currently running scope. If found, the variable corresponding to this identifier is used; If not found, search continues for the next object in the scope chain. The search continues until an identifier is found, and if a match cannot be found, the identifier is considered undefined.
Closure functions and closure objects
When functions are nested, let’s say I have A function A, and I have A v1 variable inside, and I have A function B, and I have A v1 variable in B. In order for B to execute and access v1 (in order to form a scope chain), there are two changes:
- Create A closure function that generates A closure object, A, containing the v1 variable used in B
- Push closure object A in the [[scope]] property of the B closure function.
function A(){
var v1 = 666
function B() {
return v1
}
console.dir(B)
B()
}
A()
Copy the code
The [[scope]] property of the function:
Note: It can be viewed from the Google Browser console through the Debugger. Specific how to use, you can baidu.
Now to analyze the process:
1. First, function A executes, starting with its [[scope]] chain containing only global mutable objects, and then creating an execution context object with A scope chain that initializes itself according to the copy given by [[scope]].
2. Create an active object for function A and push it into the scope chain of the execution context object.
3. When v1 is found in B, B becomes A closed function (closure), and then generates A closed object (closure) of A, holding v1 (because it exists in A and is used in B). The enclosing object is then pushed into the [[scope]] scope chain of the B function.
Note: At this point, function B has not been executed. As for the mechanism that causes JS to discover that an unexecuted function uses A variable in function A, there is A pre-parsing process involved in the v8 engine’s execution mechanism.
4, When B function executes, create the execution context, create the execution context object, initialize the scope chain of the execution context object (copy the [[scope]] property of B function).
5. Then create an active object and push it into the scope chain of the execution context object.
So, when B executes, it has access to v1, because it’s in a chain of scopes.
The change process of action chain is summarized as follows:
- When A function executes globally, the internal [[scope]] chain holds only one global variable object. Create an execution context object for the function A, copy and initialize the scope chain of the execution context object for the function A according to [[scope]].
- Create an active object for function A and push it to the front of the scope chain of the execution context object for function A.
- When you find A function inside A function (no matter how deep) that uses A variable or function inside A function.
- Then all functions in A, no matter how deep, form closure functions.
- Then create A closure object of A that contains the variables or functions to be used (by copying).
- Finally, the closure object is pushed into the [[scope]] of all closure functions.
The following conclusions can be drawn:
- There are two scope chains, one stored in the function’s [[scope]], which is used to hold the scope in case the execution context object initializes its scope chain.
- Execution of the scope chain in the context object adds the active function, the lookup of the scope chain, to look up the scope chain. (This is usually what we call the scope chain.)
- Active functions exist only in the scope chain of the execution context object.
- There are closure functions and closure objects. The [[scope]] of the closure function holds the closure object, while the closure object encloses variables or objects in the scope of the parent or grandparent function.
- Closure functions exist because the execution context is destroyed after execution, and the scope chain, active objects, variables, and so on are lost. Closure functions can hold the scope chain, and variable objects in the chain hold variables, functions, and so on.
Let’s do a harder example, and you can analyze it for yourself.
function A(){ var va = 'aaa' function B() { var vb = 'bbb' function C() { var vc1 = 'ccc' return va } function D() { var vd = 'ddd' return vb } console.dir(C) console.dir(D) C() } console.dir(B) function E () { var vd = 'eee' } console.dir(E) B() } console.dir(A) A()Copy the code
According to the above analysis, the [[scope]] of each function can be obtained:
- A has only one global variable object
- B and E have two variable objects, closure objects for A, global variable objects.
- C and D have three variable objects, closure objects for B, closure objects for A, and global variable objects.
Console:
Step through the Debugger to see live objects in the chain of scopes within each execution environment.
Anyone interested can try it.
As for closure memory leaks, there is js garbage collection involved. However, it can be seen that [[scope]] stores variables. If the memory occupied by the variable is not released, once this situation is too much, too much memory will cause memory leakage and performance problems.
Closure concept
In general, closure refers to closure functions.
To quote from Elevation (JavaScript Advanced Programming) about closures:
Closures are functions that have access to variables in the scope of another function
Through so much of the above, you taste, you fine taste…
The nature of closures
I think it’s to form a chain of scopes. You taste, you fine taste…
Here’s another interesting classic example:
function timer () { for (var i=1; i<=5; i++) { setTimeout(function(){ console.log(i); },i*1000); }} timer() // Output a 6 at intervals of one second.Copy the code
Is it not what you expected? In fact, the focus of this example is on the setTimeout function. The first argument to this function accepts a function as a callback function. This callback function does not execute immediately. This leads to the situation above.
Note: It’s wrapped in a function, so if you go through the debugger, you’ll see that it also forms a closure. The closure is each anonymous function, and the closure object is about the timer, and it holds the variable I.
You can transform this example to help you understand:
function timer () {
var i = 1;
while(i <= 5){
setTimeout(function(){
console.log(i);
},i*1000)
i = i+1;
}
}
timer()
Copy the code
Because the first function in setTimeout will not execute immediately, by the time this code is finished, I will have been assigned 6 (at 5, we enter the loop and finally add 1), so we will execute the setTimeout callback function and read the value of I. The callback function has no I in scope and reads up. The value of I in the parent scope is 6. But I * 1000 is executed immediately, so I gets the right value every time I is read.
In this case, we need a closure function to hold the different value of I for each loop.
Function makeClosures(I){// This function makes use of I in a parent scope, forming closures. var i = i; Return function(){console.log(I); return function(){console.log(I); It can access the value of I and hold the value of this I. } } function timer() { for (var i=1; i<=5; i++) { setTimeout(makeClosures(i),i*1000); // For a brief note, here makeClosures(I) are functions aware, not pass-throughs, nor a concept sures. // On each loop, makeClosures(I) are executed, forming a closure, Save closure objects containing 'I' (this is an example of five closure functions saving each closure object). // Then one anonymous function is returned each time (in this case, five anonymous functions are returned). // Every anonymous function is a local scope whose parent is makeClosures closures. // Therefore, each anonymous function reads the value of 'I', which is saved in the upper scope, and is different. So, I get the desired result}} timer() //1 //2 //3 //4 //5Copy the code
You might be somewhere else, or you might have come up with the following solution yourself:
for (var i=1; i<=5; i++) {
(function(i){
setTimeout(function(){
console.log(i);
},i*1000);
})(i);
}
Copy the code
This example not only utilizes closures, but also simulates function scope by executing functions immediately.
Do the transformation, and you see:
for (var i=1; i<=5; i++) {
function f(i){
setTimeout(function(){
console.log(i);
},i*1000);
};
f(i);
}
Copy the code
Appendix:
ES6 let: ES6 let: ES6 let: ES6 let: ES6
for (let i=1; i<=5; I ++) {// The key here is to use the let keyword to form the block-level scope setTimeout(function(){console.log(I); },i*1000); }Copy the code
I don’t know if you’re wondering why you’re using block-level scopes. I was struggling with it anyway.
Revised on 2 November 2018:
The key to this answer lies in the rules of block-level scope. It makes the variables declared by the let valid only inside {} and inaccessible externally.
Let’s do a little bit of distortion, and this is just to make it easier to understand, but it’s not:
for (var i=1; i<=5; i++) {
let j = i;
setTimeout(function(){
console.log(j);
},j*1000);
}
Copy the code
When the for(a)
uselet
, the for loop has two scopes,(a)
Parent scopes in parentheses, and{}
The subscope in parentheses.
Each loop creates a subscope. Holds values from the parent scope so that values within each child scope are different. When the anonymous function of setTimeout is executed, its scope does not have the value of I, and the I value of the subscope is read up. That’s why the value is different every time.
If you like, a block-level scope behaves just like a function scope. A subscope uses its variables, and it also forms a block-level object that is written to the function’s [[scope]].