Say first view

1. What are closures?

Phenomenon, said:

If you define another function within a function and execute the inner function after the outer function has finished executing, the inner function can also access the variables of the outer function

Function said:

If you define another function within a function, and the inner function accesses variables from the outer function, that inner function is called a closure

2. Conditions that generate closures

From the above two definitions, we can identify at least three key elements that lead to closures:

  • Element 1: Two functions have a nested relationship, that is, within one function, another function is defined
  • Element 2: An inner function is executed after the outer function has finished executing. This can take the form that the inner function returns as a return value
  • Element 3: Variables in an outer function are accessed from within an inner function. Can be arguments or internally declared variables

Let’s take an example 🌰

function createFunc(a, b) {
    let a2 = a*a;
    let b2 = b*b;
    
    return function () {
        return a2+b2; // Access the local variables of the outer function}}let fn  = createFunc(3.4); // We execute the outer function
fn() / / output is 25
Copy the code

This example satisfies the three elements that we just mentioned, and look at this!

function sumsqu(a, b) {
    
    let fn = function() {
        return a*a + b*b;
    }
    return fn();
}

sumsqu(3.4) / / 25
Copy the code

Although this code has two nested functions, the internal function fn is immediately executed as soon as it is defined, so no closure is formed.

Go a little deeper

That some partners say, you say these three elements I already know! That’s enough about closures. That’s how I understood it, until one day an interviewer asked me how closures work 😂!

To understand how closures work, we need to introduce concepts such as scope, static scope, and execution context. With these concepts, we can analyze the flow of code execution to make it clearer

1. Scope

Scope: scope is the search area for an identifier. What are identifiers: variable names and function names. Three types of scope are supported in JS: global scope, function scope and block-level scope

For example 🌰 :

    let a = 100;  / / 1
    function log() { / / 2
        {
            let b = 10; / / 3
        }  
        let c = 12;    / / 4
        console.log(a+b+c); / / 5
    }
    log(); 
Copy the code

I annotated the code with numbers to illustrate the flow:

  1. So in this code we’ve defined a and the log function and they’re in the global scope, so you can think of the global scope as a big box
  2. On the last line we call the log function, which produces an identifier lookup. Since foo() is at the outermost layer of the code, its lookup is global, and it can quickly find function foo
  3. Inside the log function, we define a b = 10 at 3, which is enclosed in curly braces. This creates a new scope (block-level scope), which is a small box with only one B, invisible to code outside the box
  4. At 4, we define a variable c, which is clearly defined in the log function scope, and the accessible scope of C is this function
  5. At 5, it’s a little bit more complicated. First of all, it’s a function call, and the JS engine looks for the console identifier, and it doesn’t find it in function foo, so the JS engine looks up and goes into the global scope, and in the global scope, console is the built-in object, The search for the log property is similar to that for Console
  6. Note that b cannot be found, and this code will eventually report an error because the identifier lookup only looks up to the parent

2. Static scopes and scope chains

From the above analysis, it is not difficult to find that we can clearly know the scope of the code by analyzing the location of functions and variables in the code. That is, whenever we call the function log, the access rules for the variables in the function already determine that the scope of the function depends only on the code structure of the function. And scopes are nested with each other.

It is based on these nested scopes that identifiers are searched from the inside out to the global scope!

For the nesting of scopes, see the following figure:

Here’s a summary:

  1. This scoping mechanism based on code structure analysis is called static scoping or lexical scoping. It is implemented by THE JS engine according to the ECMAScript-262 standard
  2. We call the rule of scope nesting and identifiers look-up from inside out scope chain, meaning that every look-up of an identifier is looked up from inside out scope chain

3. The relationship between static scopes and closures

What does the scope you’ve been talking about have to do with my closure 🙄!

Closures, in my opinion, are a means of implementing static scopes! The standard of JavaScript is made by the TC39 committee, and the implementation is made by the wave of people who write JavaScript engines! I seem to hear their dialogue, “we have worked out the standard, how to achieve it depends on you!”

Let’s go back to the closure example at the beginning of this article!

function createFunc(a, b) {
    let a2 = a*a;
    let b2 = b*b;
    
    function getSum() {
        return a2+b2; // Access the local variables of the outer function
    }
    
    console.dir(getSum) // Prints the function getSum
    
    return getSum;
    
}


let fn  = createFunc(3.4); // We execute the outer function
fn() / / output is 25
Copy the code

Let’s do a quick analysis based on what we just said about static scopes

  1. There are two scopes in this codecreateFuncFunction scope, andgetSumFunction scope, two scopes nested with each other
  2. In the getSum function that accesses the outer scopea2 和 b2Two variables,
  3. According to the rules of the scope chain, it should be accessible in the function getSuma2 和 b2
  4. whencreateFuncWhen the execution is complete,getSumIt’s not implemented
  5. Normally, variables in a function are released when the function completes execution. But there is one exception
  6. And in order to do that, we have to find a placea2 å’Œ b2Exists in the functiongetSumTo comply with JavaScript’s static scoping rules

Where does that exist? In the getSum function, there is an internal property [[Scopes]]. Run this in Chrome:

Closure(createFunc) with two values A2 =9 and b2=16. See if the Closure name is created by createFunc() and attached to getSum.

On the right! Yeah, that’s it.

It feels like it’s getting closer and closer to principle!

Let’s redefine closures:

A closure is a set of unfreed variables (or regions of memory) that are actually hanging on a function and are used when the function executes!

A few more points:

  1. Closures are created during lexical analysis of functions. To save memory, only the necessary values are kept in closures, which are the variables used in the function’s execution!

Use of closures

1. Anti-shake and throttling

Post a code for you to feel

/ / image stabilization
function debounce(fn, dealy = 300) {
  let timer;
  return function () {
    const args = arguments;
    if (timer) {
      clearTimeout(timer);
    }
    timer = setTimeout(() = > {
      fn.apply(this, args);
    }, delay);
  };
}

/ / throttling
function throttle(fn, delay) {
  let flag = true;
  return function () {
    let args = arguments;
    if(! flag)return;
    flag = false;
    timer = setTimeout(() = > {
      fn.apply(this, args);
    }, delay);
  };
}

Copy the code

2. Generate consecutive ids

let uniqueId = function() {
    let start = 0;
    return function (prefix = 'id_') {
        let id = ++start;
        if(prefix === 'id_') {
            return  `${id}`;
        }
        return `${prefix}${id}`
    }
}();
Copy the code

If a closure function is not used, it can be released in time to avoid excessive memory consumption. If the closure function is assigned null, it can be released.

conclusion

  1. The essence of a closure is a set of variables (or areas of memory) that are not freed and added to the execution context when the function is executed!
  2. Closure causes: BecauseJavaScriptAn implementation mechanism that follows static scoping rules to ensure that functions can access variables in the outer scope during execution

reference

  1. On JavaScript you Don’t Know
  2. Closures – JavaScript | MDN
  3. Interviewer: Talk about scopes and closures
  4. JavaScript’s static scope chain versus the “dynamic” closure chain

The last

Thank you for reading this, and I hope you have a better understanding of closures after reading this article!

If you think you’ve gained something, give it a thumbs up and go