“This is the first day of my participation in the First Challenge 2022. For details: First Challenge 2022”

An important prerequisite to understanding closures is scope. So if you don’t know a lot about scopes, I suggest you read the article “Getting scopes inside out.”

What is a closure

Closures are functions that have access to variables in the scope of another function. A function that refers to a variable in the scope of another function. So closures are usually formed in function nesting. For example, the following code forms a closure:

Function foo (){var name = 'snail' return function(){console.log('my name is '+name)}}Copy the code

Closures are not a tool or technique that requires learning a new syntax or pattern to use; they are a natural consequence of writing code based on lexical scope. You don’t even need to create closures in order for closures to be created; once you know closures, you’ll see closures everywhere in your code. You understand closures so that you can identify and influence their use as you wish. Let’s take a look at common application scenarios for closures.

Application scenarios

Implement block-level scope

Let’s start with this code:

function foo(){ var result = []; for(var i = 0; i<10; i++){ result[i] = function(){ console.log(i) } } return result; } var result = foo(); result[0](); // 10 result[1](); / / 10Copy the code

As you can see, each function does not print 0 as expected result[0](), result[1]() prints 1, and so on. Because the I declared by var doesn’t just belong to every current loop, or even to the current for loop, since there is no block-level scope, the variable I is promoted to the scope of function foo. So every function holds the same variable I in its scope chain, and when we execute the child functions in the array, the loop inside Foo is finished, and I = 10, so every function call prints 10.

We then add a layer of immediate functions (IIFE) inside the for loop, creating a new closure that holds the loop’s I inside, so when we execute the array function again, the result[0]() prints 0 as expected, Result [1]() Print 1…

function foo(){ var result = []; for(var i = 0; i<10; i++){ (function(i){ result[i] = function(){ console.log(i) } })(i) } return result; } var result = foo(); result[0](); // 0 result[1](); / / 1Copy the code

Of course, ES6 introduced the let variable declaration approach, which allows JavaScript to have block-level scope, to solve such a problem more easily.

Save internal state

Let’s start with this code:

function cacheCalc(){ var cache = new Map() return function (i){ if(! cache.has(i)) cache.set(i,i*10) return cache.get(i) } } var calc = cacheCalc() console.log(calc(2)) // 20Copy the code

As you can see, the function internally uses a Map to hold the computed result (or any other data structure), which is computed only if the input number has not been computed, otherwise the previous computed result is returned, thus avoiding double-counting.

This technique is also used in the Vue3 source code. Here I added some comments while reading the source code, which caused the line numbers in the screenshot to be inconsistent with those in the source file, but the code did not make any changes.

The compileToFunction will compile the template to produce the render function, and to avoid double compilations we create an internal compileCache object to hold the compiled data.

The function is currified

First of all, what is a function Corrification? Currization is changing a function that takes multiple arguments to a function that takes a single argument (the first argument of the original function) and returns a new function that takes the remaining arguments and returns the result. It is a technology that can decompose a function that accepts multiple parameters into multiple functions that receive a single parameter. The logic of the original function will not be executed until the number of parameters received is satisfied. For example, a very classic interview question => implement add(x)(y)(z) = x+y+z is used in the function of currization. The code is as follows:

function add(x){
  return function(y){
    return function(z){
      return x+y+z
    }
  }
}
console.log(add(1)(2)(3)) // 6
Copy the code

Another example is that we have a function foo that saves the input number to two decimal places. At this point, we need a function that keeps the input number two decimal places and adds commas every three places. At this point, we can introduce the function foo and add commas every three places to the previous result.

The module pattern

Let’s start with this code:

function create(){ var name = 'snail', hobby = ['eat','sleep','codeing'] function say(){ console.log('my name is '+name+'.') } function showHobby(){ console.log(name+' like '+hobby.join(',')+'! ') } return { say, showHobby } } var instance = create(); instance.say(); // my name is snail. instance.showHobby(); // snail like eat,sleep,codeing!Copy the code

This pattern is called a module in JavaScript. Here we call the create function to create an instance of the module, which contains references to internal functions. This ensures that the internal data variables are hidden and private, and the return value can be viewed as the API exposed by the module. It is also possible to return methods directly as the module’s public API, as JQuery returns $.

disadvantages

Because closures carry the scope of the function that contains them, even when the outer function is finished executing, the inner function holds references to variables in the outer function, so overusing closures can cause excessive memory usage.

Ok, so that’s all I have to say about closures in this article. <( ̄)  ̄)>

If you have any questions or suggestions, please leave a comment! 👏 🏻 👏 🏻 👏 🏻