Closures are a cliche, but what are the core points of understanding closures?

Static scope

To begin with, understand the definition of static scope: the scope of a function is determined at definition time.

You can see that a closure is a static scope. The inner function looks up the required variable where it is defined.

Case 1:

let a = 1
function fn() {
    let a = 0
 function fn2() {
    console.log(a);
 }
  fn2()
}
fn() / / 0
Copy the code

Example 2:

let a = 1
function fn() {
   let a = 0
   fn2()
 }
function fn2() {
   console.log(a);
}
fn() / / 1
Copy the code

Based on the above two examples, the characteristics of static scope can be well demonstrated. Example 1: When code is defined, determine its scope. At run time, the execution environment is created according to the scope. When a is printed in the fn2 function, it is checked to see if the current fn2 scope exists. If not, it will go up the scope chain to the fn function scope and find the first match of the variable to stop. So, a outputs the value 0 in FN.

In example 2, again, fn2 looks for the scope above it, which is the value 1 of variable A in the global scope.

Closure definition

Closure: First, a closure is an object. It is an object whose internal nested function refers to an external function variable (function). It is a combination of internal functions bundled with references to the surrounding state.

See the closure

The Chrome console debugs, executes the external functions, and finds that the variables required by the internal functions are present in the [[scopes]].

As shown in the figure above, a closure exists in an internally nested function that contains the referenced variable A.

Common closures

There are two common closures: one that takes a child function as the return value of a parent function, and one that passes arguments from a parent function to a child function.

The child function is the return value of the parent function

let a = 1
function fn() {
  let a = 0
  function fn2() {
    a++
    console.log(a);
  }
  return fn2
}
let f = fn()
f() / / 1
f() / / 2
Copy the code

Let f = fn(); F () calls the fn2 function inside fn. Execute fn2: fn2 finds the variable a referenced in fn through the scope chain, executes a++, and prints 1. Execute the f() statement again, calling fn2 again. The variable inside fn is still there, but after the last self-addition, the value of a is 1. Fn2 is again a++, and the output value is 2.

Note: The number of closures called depends on how many times the external function is called.

Arguments from a parent function are passed to a child function

function fn(a) {
 function fn2() {
    a++
    console.log(a);
 }
 fn2()
 fn2()
}
fn(0) / / 1. 2
Copy the code

The fn2() internal function references the fn external function argument. The first time you run fn2(), a adds itself and prints 1; The second time you run fn2(), the value of a is already 1, and the output of a is 2.

The closure effect

  • Using variables inside the parent function to live in memory after the function has completed (extending the life of local variables)
  • External can call the child function, to operate on the parent function internal data (variables/functions)
function fn() {
  let a = 0
  function fn2() {
    a++
    console.log(a);
  }
  return fn2
}
let f = fn()
f()
Copy the code

1.fn()After execution, the variable A still lives in memory.Explanation: In executionlet f = fn()The variable f points to the function object that fn2 points to. After this statement completes, the fn2 function variable is released. But the function object to which f points (the closure) still exists, and the variable A inside the closure still lives in memory. To performf()Is equivalent to calling the function object in the figure above.

If the variable f is not defined to point to the closure, then only fn() is executed. So, the inner function releases the collection when the FN completes. Fn internal functions cannot be called externally.

Therefore, after the execution of the parent function, the local variables declared inside the parent function generally do not exist, and the variables in the closure may exist.

2. The internal variable a of fn can be added externally by manipulating the variable f.

The lifecycle of closures

Generation: occurs (not called) when a subfunction is defined. Death: Occurs when an object inside a subfunction becomes a garbage object

function fn() {
  let a = 0
  function fn2() {
    a++
    console.log(a);
  }
  return fn2
}
let f = fn()
f()
f()
Copy the code

Inside FN, at line 39, the closure is present when fn2 is defined.Code execution is complete, and the closure still exists!

At this point, we can make the function object that fn2 points to garbage by setting the f variable to null. addf = null.

Closure application

The most common use of closures is in defining JS modules. Define a JS module that exposes multiple methods or variables. Variables that are used inside the module are an application of closures if the method is exposed. You can define a simple JS module like this:

; (function (window) {
  let a = 0
  function f1() {
    console.log('f1', a)
  }
  function f2() {
    console.log('f2', a)
  }
  window.myModule = { f1, f2 }
})(window)
Copy the code

Directly import the files where needed and call the available methods of myModule. For example, mymodule.f1 () can execute the method of f1 in the module.

The myModule global object is exposed by using anonymous functions in the js module. You don’t need to execute myModule when using myModule. You can call myModule methods directly. The window parameter is passed, usually to abbreviate the window parameter when the code is compressed.

Closure disadvantages and solutions

Faults: Easy to cause logic problems; After the function is executed, the internal variables of the function are not released and occupy memory for a long time, which may cause memory leaks.

Loops and closures

Closures can cause logic problems. Here’s a typical example:

for (var i = 0; i <= 5; i++) {
  setTimeout(function timer() {
     console.log(i);
  }, 1000)}// 6 6 6 6 6 6 6
Copy the code

The for statement in JS does not belong to the block scope and defines the global variable to which I belongs. All six loops are enclosed in the shared global scope, so there is only one I.

Again, setTimeout is an asynchronous callback. SetTimeout is executed after the loop has finished six times.

The above code is equivalent to defining the setTimeout function callback six times, without using a loop at all.

What if you want to output 0, 1, 2, 3, 4, 5?

Solution: Core: Use the respective scope of closed loops.

  • Use IIFE (execute function expressions immediately) to close the scope created in each iteration.
  • Using the let declaration, you can hijack the block scope and declare the variable in the block scope.

A memory leak

Memory leak: An allocated memory is neither usable nor recycled to the browser until the process ends. For memory leaks, see Closure Life Cycle, where closures manually release reclaims.

Solution:

  • Don’t use closures when you can
  • Let the inner function be called a garbage object and reclaim the closure (released in time).

Added: Memory overflow and memory leak

Relationship between memory overflow and memory leak: Memory leaks accumulate to a certain extent, resulting in memory overflow.

A memory leak

Memory leaks:

  • closure
  • Unexpected global variables
function fn() {
   a = 0
   console.log(a);
}
fn()
Copy the code

The variable a is undefined and becomes a global variable. After the code is executed, the a variable remains in the global variable.

  • setInterval
setInterval(() = > {
  console.log(The '-');
}, 1000);
Copy the code

The cycle timer was not cleared in time. Procedure You need to use clearInterval to manually clear the data.

let time = setInterval(() = > {
  console.log(The '-');
}, 1000);
clearInterval(time)
Copy the code

Out of memory

A memory overflow error is thrown when a program needs more memory to run than is available.

Executing the above code, the browser has been stuck in the running state, the memory is not enough for the program.