closure

JavaScript closures are everywhere; you just need to be able to recognize and embrace them.

The essence of closures

define

Closures occur when a function can remember and access its lexical scope, even if the function is executed outside the current lexical scope.

 function foo() {
   var a = 2
   
   function bar() {
     console.log(a)  // 2
   }
   
   bar()
 }
 foo()
Copy the code

In the code above, the function bar() has a closure that covers the scope of foo() (virtually all scopes it can access).

The principle of

To see how closures work more clearly, let’s make some changes to the code.

Function foo() {var a = 2 function bar() {console.log(a)} return bar} var baz = foo() baz() // 2 -- that's what closures doCopy the code

In this example, we treat the function object referenced by the function bar as the return value itself.

After foo() is executed, its return value (that is, the internal bar) is assigned to baz and baz() is called, essentially calling the internal function bar() with different identifier references.

Because the engine has a garbage collector to free up memory space that is no longer used, generally the entire internal scope of foo() is destroyed when foo() completes execution.

Closures prevent that from happening. Because bar() has a closure that covers the internal scope of foo(), that scope can always be alive for bar() to reference at any time thereafter. This reference is called a closure.

The function is called outside the lexical scope of the definition, and the closure allows the function to continue to access the lexical scope of the definition.

Loops and closures

A for loop is a common example of a closure.

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

The expected output is 1-5 per second.

But the actual output is five sixes at a rate of one per second.

Analyze the code above: the loop terminates if I is no longer <=5, and the first time the condition is true is 6, so the output shows the final value of I at the end of the loop, 6.

Digging deeper into the cause of this defect, the five functions in the loop are defined separately in each iteration, but they are all enclosed in a shared global scope, so there is really only one I. Ideally, each iteration in the loop “captures” itself a copy of I at run time.

Let’s start solving this problem.

Method one: We need to create a closure scope for each iteration of the loop.

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

Using the immediate execution function, we pass in the I.

Using an immediate execution function within an iteration generates a new scope for each iteration, allowing the callback of the delay function to enclose the new scope within each iteration, and each iteration will contain a variable with the correct value for us to use.

Method two: use block-level scope and let to declare variables.

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

The let declaration in the for loop, which states that variables are declared not just once during the loop, but each iteration. Each subsequent iteration initializes this variable with the value at the end of the previous iteration.

Closures in modules

The most common way to implement modules is often referred to as module exposure, and the following code shows a variation:

function CoolModule() { var something = "cool" var another = [1, 2, 3] function doSomething() { console.log(something) } function doAnother() { console.log(another.join("!" )) } return { doSomething, doAnother } } var foo = CoolModule() foo.doSomething() // cool foo.doAnother() // 1! 2! 3Copy the code

Analyze the above code:

  1. You need to callCoolModule()To create a template instance. Neither the inner scope nor the closure can be created without executing an external function.
  2. CoolModule()Returns an object. The returned object contains references to internal functions rather than internal data variables. You can think of the return value of this object type as essentially the module’s public API.
  3. doSomething()doAnother()Functions have closures that cover the internal scope of a module instance.

We can convert the module function to IIFE, call this function immediately and copy the return value directly to the simple module instance identifier foo.

var foo = (function CoolModule() { var something = "cool" var another = [1, 2, 3] function doSomething() { console.log(something) } function doAnother() { console.log(another.join("!" )) } return { doSomething, doAnother } })() foo.doSomething() foo.doAnother()Copy the code

Refer to the

JavaScript You Don’t Know

summary

Closures occur when a function can remember and access its lexical scope, even if the function is executed outside the current lexical scope.

Modules have two characteristics:

  1. A wrapper function is called to create the inner scope;
  2. The return value of the wrapper function must include at least one pair of references to the inner function, so that a closure covering the entire inner scope of the wrapper function is created.