Replenish:
Closure is a difficulty in the Javascript language and its feature. Many advanced applications rely on closures to implement.
Closure features
Closures have three characteristics:
1. Function nested functions
2. The function can refer to external parameters and variables inside
3. Parameters and variables will not be collected by the garbage collection mechanism
Definition of closure and its advantages and disadvantages
Closures refer to functions that have access to variables in the scope of another function. The most common way to create closures is to create another function within one function and access local variables of this function through another function.
The disadvantage of closures is that they are resident memory, which will increase the memory usage, and improper use can easily lead to memory leakage.
Closures are a major feature of the JavaScript language. The main application of closures is mainly for: designing private methods and variables.
After the general function is executed, the local active object is destroyed and only the global scope is saved in memory. But the closure situation is different!
To talk about the topic
1. Definition of closure?
Let’s take a look at some definitions about closures:
1. Closure refers to a function that has permission to access variables in another function scope.
2. Function objects can be associated through scope chains, and variables inside the function body can be saved in the scope of the function. This characteristic is called 'closure'.
3. Internal functions can access parameters and variables of external functions that define them (except this and arguments).
If you want to systematically learn the concept of js closure, you can refer to the js e-book column of Wulin.com website to learn.
Let's summarize the definition
1. You can access functions of variables in external function scope
2. The variables of external functions accessed by internal functions can be saved within the scope of the external function without being recycled--this is the core. Later, when we encounter closures, we should think of it. We should focus on the variable referenced by the closure.
Create a simple closure
var saysName = function(){var name = 'jozo';return function(){alert(name);}};var says = sayName(); say();Let's interpret the next two sentences:
•var says = sayName(): Returns an anonymous internal function stored in the variable say and refers to the variable name of the external function. Due to the garbage collection mechanism, after the sayName function is executed, the variable name is not destroyed.
•say(): Execute the returned internal function, and still access the variable name and output 'jozo'.
2. Scope chain in closure
Understanding scope chains is also helpful for understanding closures.
The search methods of variables in scope should be very familiar, but in fact, this is what is searching upwards along the scope chain.
When the function is called:
1. First create an execution context and the corresponding scope chain;
2. Add arguments and other named parameters values to the function's active object (activation object)
Scope chain: The active object of the current function has the highest priority, followed by the active object of the external function, and the active object of the external function of the external function decreases in sequence until the end of the scope chain - the global scope. Priority is the order of variable searches;
Let’s first look at a normal scope chain:
function saysName(name){return name;}var says = sayName('jozo');This code contains two scopes: a. global scope; b.sayName function scope, that is, there are only two variable objects. When executed to the corresponding execution environment, the variable object will become an active object and be pushed to the front end of the scope chain of the execution environment, that is, it becomes the highest priority. Talk to the picture:
This picture is also available in JS advanced programming books, and I have re-drawn it all.
When creating the sayName() function, a scope chain containing the variable object in advance is created, that is, the scope chain indexed by 1 in the figure, and is saved in the internal [[Scope]] attribute. When the sayName() function is called, an execution environment is created, and then the scope chain is built by copying the object in the [[Scope]] attribute of the function. After that, another active object (indexed by 0 in the figure) is created and pushed into the front end of the scope chain of the execution environment.
Generally speaking, when the function is executed, the local active object will be destroyed and only the global scope is saved in memory. However, the situation of closures is different:
Let’s take a look at the scope chain of closures:
function saysName(name){return function(){return name;}}var says = sayName('jozo');This closure instance has one more scope for anonymous function than the previous example:
After the anonymous function is returned from the sayName() function, its scope chain is initialized to an active object and a global variable object containing the sayName() function. In this way, the anonymous function can access all variables and parameters defined in sayName(). More importantly, after the execution of the sayName() function, its active object will not be destroyed, because the scope chain of the anonymous function still refers to the active object. In other words, after the execution of the sayName() function, the scope chain of its execution environment will be destroyed, but its active object will be left in memory, knowing that the anonymous function will be destroyed. This is also the memory leak problem that will be discussed later.
I don't write so much about scope chain issues, and writing things in the book is also very tiring o(□)o
3. Example of closure
Example 1: Implementation of accumulation
// Method 1var a = 0;var add = function(){a++;console.log(a)}add();add();// Method 2: Closure var add = (function(){var a = 0;return function(){a++;console.log(a);}})();console.log(a); //undefinedadd();add();In comparison, Method 2 is more elegant, and also reduces global variables, and privatizes variables.
Example 2: Add click event to each li
var oli = document.getElementsByTagName('li');var i;for(i = 0;i < 5;i++){oli[i].onclick = function(){alert(i);}}console.log(i); // 5//Execute anonymous function(function(){alert(i); //5}());The above is a classic example. We all know that the execution result is that 5 pops up, and we also know that closures can be used to solve this problem, but at the beginning, I still can't understand why 5 pops up and why closures can solve this problem. Later, I sorted it out and made it clear:
a. Let's first analyze the situation before the closure is used: in the for loop, we bind an anonymous function to each li click event, and the value of variable i returns in the anonymous function. When the loop ends, the value of variable i becomes 5. At this time, we click on each li, that is, execute the corresponding anonymous function (see the code above). This is the variable i is already 5, so each click pops up 5. Because each anonymous function returned here refers to the same variable i, if we create a new variable to save the current i value of i when the loop is executed, then let the anonymous function apply this variable, and finally return this anonymous function, so that our purpose can be achieved. This is achieved using closures!
b. Let’s analyze the situation when using closures:
var oli = document.getElementsByTagName('li');var i;for(i = 0;i < 5;i++){oli[i].onclick = (function(num){var a = num; // To illustrate the problem return function(){alert(a);}})(i)}console.log(i); // 5When the for loop is executed, the anonymous function bound to the click event is passed i and executed immediately to return an internal anonymous function. Because the parameters are passed by value, the formal parameter num saves the current value of i, and then assigns the value to the local variable a. Then the internal anonymous function keeps the reference of a, that is, keeps the current value of i. So after the loop is executed, click each li, and the returned anonymous function will pop up the value of the saved a.
4. Application of closures
Let's take a look at the purpose of closures. In fact, by using closures, we can do a lot of things. For example, simulate object-oriented code style; express code more elegantly and concisely; and improve code execution efficiency in some aspects.
1. Anonymous self-executing function
In actual situations, we often encounter a situation where some functions only need to be executed once and their internal variables do not need to be maintained, such as UI initialization, so we can use closures:
//Change all li fonts to red (function(){ var els = document.getElementsByTagName('li'); for(var i = 0,lng = els.length;i < lng;i++){els[i].style.color = 'red';} })();We create an anonymous function and execute it immediately. Since the external cannot refer to the variables inside it, local variables such as els, i, and lng will be released soon after execution, saving memory!
The key is that this mechanism will not pollute the global object.
2. Implement encapsulation/modular code
var person= function(){ //The scope of the variable is inside the function, and the var name = "default" cannot be accessed outside. return { getName : function(){ return name; }, setName : function(newName){ name = newName; } } }();console.log(person.name);//direct access, the result is undefined console.log(person.getName()); //default person.setName("jozo"); console.log(person.getName()); //jozo3. Implement object-oriented
In this way, different objects (instances of classes) have independent members and states and do not interfere with each other. Although there is no such mechanism as class in JavaScript, by using closures, we can simulate such mechanisms. Let’s talk about the above example:
function Person(){ var name = "default"; return { getName : function(){ return name; }, setName : function(newName){ name = newName; } } }; var person1= Person(); print(person1.getName()); john.setName("person1"); print(person1.getName()); // person1 var person2= Person(); print(person2.getName()); jack.setName("erson2"); print(erson2.getName()); //person2Person's two instances person1 and person2 do not interfere with each other! Because these two instances have independent access to the name member.
5. Memory leaks and solutions
Garbage recycling mechanism
Speaking of memory management, it is naturally inseparable from the garbage collection mechanism in JS. There are two strategies to realize garbage collection: mark clearance and reference counting ;
Mark removal: When the garbage collector runs, it will mark all variables stored in memory. Then, it will remove the tags of variables in the environment and the tags of variables referenced by variables in the environment. After that, if the variable is marked again, it means that the variable is ready to be deleted. Until 2008, IE, Firefox, Opera, Chrome, and Safari's JavaScript have all used this method;
Reference count: Track the number of times each value is referenced. When a variable is declared and a value of a reference type is assigned to the variable, the number of times this value is referenced is 1. If this value is assigned to another variable, the number of times a reference is increased by 1. On the contrary, if a variable deviates from the reference of the value, the number of references of the value is reduced by 1, and when the number of times is 0, it will wait for the garbage collector to recycle.
A big problem with this method is circular reference, that is, object A contains a pointer to B, and object B also contains a reference to A. This can cause a large amount of memory to be recycled (memory leaks), because their references can never be 0. In the early IE versions (ie4-ie6) adopted a garbage collection mechanism that was counted. One of the reasons why closures caused memory leakage was a flaw of this algorithm.
We know that some objects in IE are not native javascript objects. For example, objects in BOM and DOM are implemented in the form of COM objects, and the garbage collection mechanism of COM objects adopts reference counting. Therefore, although IE's javascript engine adopts a tag clearing strategy, accessing COM objects is still based on reference counting, so as long as COM objects are designed in IE, there will be a problem of circular references!
Take a chestnut:
window.onload = function(){var el = document.getElementById("id");el.onclick = function(){alert(el.id);}}Why does this code cause memory leaks?
el.onclick= function () {alert(el.id);};When executing this code, the anonymous function object is assigned to the onclick attribute of el; then the anonymous function refers to the el object inside, and there is a circular reference, so it cannot be recycled;
Solution:
window.onload = function(){var el = document.getElementById("id");var id = el.id; //Unreference el.onclick = function(){alert(id); }el = null; //Clear the active object in the external function referenced by the closure}The above is the summary of the relevant knowledge about JS closure scope chain garbage collection memory leak introduced to you by the editor. I hope it will be helpful to everyone!