This article mainly introduces closures and explains under what circumstances memory leaks will occur. There is also an interview question about closures. The function in which the closure is located is called multiple times, and the variables in the closure are created several times.
What is closure
1.Definition
If within an inner function, a reference is made to a variable in the outer scope (but not in the global scope), then the inner function is considered a closure. To put it simply, a closure is a function, but it is inside other functions. In essence, closures are a bridge between the inside of a function and the outside of the function.
A simple understanding of the meaning of closure is to allow us to indirectly access variables inside functions, extend the service life of variables and reduce namespace pollution.
function createClosure(){<!-- --> var name = "jack"; return {<!-- --> setStr:function(){<!-- --> name = "rose"; }, getStr:function(){<!-- --> return name + ":hello"; } } } var builder = new createClosure(); builder.setStr(); console.log(builder.getStr()); //rose:hello
The above function reflects two closures. Both closures maintain references to the external scope, so no matter which one is called, the variables in the external function can be accessed. For functions defined inside a function, the closure will add the free object of the external function to its own scope, so the properties of the external function can be accessed through the internal function. This is a way for js to simulate private variables.
2. Disadvantages caused by:
1). Closures occupy more memory than other functions, and multiple uses will lead to an increase in memory usage. Closures have a negative impact on script performance in terms of processing speed and memory consumption.
2). Due to the influence of the action chain, the closure can only obtain the last value of the internal function. What is reflected here is also the piece of code that appeared above.
var data = []; for (var i = 0; i < 3; i + + ) {<!-- --> data[i] = function () {<!-- --> console.log(i); }; } data[0](); // 3 data[1](); // 3 data[2](); // 3
That
2 Purpose
Access function internal variables
Preserving state: Closures can be used to maintain state between function calls. Closures allow a function to remember its previous state each time it is called. This is useful in certain situations where state needs to be maintained across function calls, such as counters, caches, etc.
Simulate private variables: Closures can be used to create private variables that are invisible to the outside and can only be accessed and modified through functions inside the closure. This kind of encapsulation can help us achieve information hiding and data protection purposes
Modular development: Closures can be used to create modular code structures. By encapsulating variables and functions in closures, we can avoid them from conflicting with other code in the global scope and achieve information isolation and code reuse between modules.
3. Solve closure side effects
Due to the influence of the action chain, the closure can only obtain the last value of the internal function. If the internal function is in a loop, then the value of the variable is always the last one. The code above will not be repeated. If you want to solve this problem, there are three ways :
Method 1: Execute the function immediately
for (var i = 0; i < 3; i + + ) {<!-- --> (function(num) {<!-- --> setTimeout(function() {<!-- --> console.log(num); }, 1000); })(i); }
Method 2: Return an anonymous function assignment
var data = []; for (var i = 0; i < 3; i + + ) {<!-- --> data[i] = (function (num) {<!-- --> return function(){<!-- --> console.log(num); } })(i); } data[0](); // 0 data[1](); // 1 data[2](); // 2
This principle is the same as that of the immediate execution function. It is because the variable is passed by value, so the i value of the variable is assigned to the actual parameter num. An anonymous function is created inside the anonymous function to access num, so that each function Each has a copy of num and does not affect each other.
Method 3: Use ES6 let
var data = []; for (let i = 0; i < 3; i + + ) {<!-- --> data[i] = function () {<!-- --> console.log(i); }; } data[0](); data[1](); data[2]();
When looping, let declares i, so the entire block is block-level scope, then the function data[0] becomes a closure, expressed here with {}, just hoping to use it to explain that when let exists, this for Loop blocks are block-level scoped, not global scoped.
Under what circumstances will closure cause memory leaks
Closures can cause memory leaks in some cases, mainly because closures prevent variables from being garbage collected, thus occupying unnecessary memory. Here are some situations that can cause memory leaks:
1. Recycling
If the closure internally references variables in the outer scope, and these variables directly or indirectly refer to the closure itself, a circular reference will be formed. In this case, even if the closure is no longer used, the garbage collection mechanism cannot release the closure and the variables it refers to due to the existence of circular references, resulting in a memory leak.
function createClosure() {<!-- --> var data = "Some data"; return function() {<!-- --> console.log(data); }; } var closure1 = createClosure(); var closure2 = createClosure(); closure1.otherClosure = closure2; closure2.otherClosure = closure1;
2. Failure to release the closure in time:
If the closures created are not released in time, even if they are no longer needed, it will cause a memory leak. This often happens in long-running applications, especially when using event handlers. If closures are bound to DOM elements as event handlers but are not unbound in a timely manner, these closures will remain in memory and cannot be released.
function setupEventHandlers() {<!-- --> var element = document.getElementById("myElement"); element.addEventListener("click", function() {<!-- --> console.log("Clicked"); }); } setupEventHandlers();
In order to avoid memory leaks caused by closures, we should pay attention to the following points:
Try to avoid circular references and ensure that the reference relationship between the closure and external variables is linear rather than forming a circular chain.
Unbind the event handler promptly to ensure that the closure can be garbage collected when it is no longer used.
Pay attention to the life cycle of the closure and ensure that the closure is released in time when it is not needed.
Minimize the use of closures and avoid potential memory leaks caused by over-reliance on closures.
The variables in the closure will be recycled by the garbage collection mechanism when they meet the following conditions:
The closure is no longer referenced: If the closure is no longer referenced by any other code, that is, no variable or function holds a reference to the closure, then the closure and its internal variables can be recycled by the garbage collection mechanism.
The variables in the closure are not referenced by other code: If the variables in the closure are no longer referenced by other code, then these variables can also be recycled by the garbage collection mechanism. The garbage collection mechanism will detect whether there are other references to the variable. If not, it will mark it as recyclable and recycle it at the appropriate time.
It should be noted that the variables in the closure will only be recycled when the above conditions are met. If the variables in the closure are still referenced by other code, or the closure itself still has references, then these variables will not be recycled and they will continue to exist in memory.
function createClosure() {<!-- --> var data = "Some data"; return function() {<!-- --> console.log(data); }; } var closure = createClosure(); closure(); // Output: "Some data" closure = null;
After executing closure = null, no other code refers to the closure variable, and both the closure function and the data variable in it can be garbage collected.
The function in which the closure is located is called multiple times, and the variables in the closure will be created several times
Each call will create a new closure instance and create a new execution environment, which contains the variables and parameters inside the function. The closure saves the variables in the function by referencing the environment in the execution environment of the function. so that these variables can still be accessed and used after the function ends
function outer() {<!-- --> var counter = 0; return function inner() {<!-- --> counter + + ; }; } outer() // 1 outer() // 1
If you want to persist the closure instance, you can save it in a variable
function outer() {<!-- --> var counter = 0; return function inner() {<!-- --> counter + + ; console.log(counter); }; } var closure1 = outer(); closure1(); // Output: 1 closure1(); // Output: 2 var closure2 = outer(); closure2(); // Output: 1