Scope, scope chain, execution context

To understand closures, you must first understand the scope, scope chain, and execution context. If you don’t understand all of these, you don’t need to look at closures. Take a look at this big guy’s article. It’s very detailed

In-depth understanding of JavaScript scopes and scope chains

To summarize the key points:

  1. Scopes are layered, with inner scopes having access to variables in outer scopes and not vice versa
// block scope {let a = 'parent scope of a' {let b =' child scope of b' console.log(' child get parent of a', } console.log(' parent gets child b ', 'parent gets child B', Function father() {var a = 'father' {var b = 'son' {var b = 'father'} function father() {var b = 'father' {var b = 'son'} Console. log(' the child gets the parent's A ',a) // the child gets the parent's a // The child can get the parent's variable} son() console.log(' the parent gets the child's B ', } father();} father();} father();} father();Copy the code
  1. When a function looks up a variable level by level, it looks up the chain of scopes that created the function, not the chain of scopes that called the function, and that’s called static scope.
Var x = 10 function fn() {console.log(x)} function show(f) {var x = 20 (function() {f() //10, not 20. X})()} show(fn)Copy the code

Note: If you define a variable directly without using var, you define the variable under the window of the global environment. For example:

Function fn() {test = '111' testFn = function () { {... } return `test` } } fn() console.log(test, window.test, window.test === test) //111 111 true console.log(testFn(), window.testFn(), testFn() === window.testFn()) //test test trueCopy the code
  1. Then understand the execution context, which is created each time the function is called. After execution, the execution environment is automatically destroyed if other code no longer refers to a variable in the method.

    When HTML runs, a global execution environment is created that is never destroyed, and all variables and functions defined in this environment are not automatically destroyed

  2. The biggest difference between scope and execution context is that the execution context is determined at run time and can change at any time; The scope is defined at definition and does not change.

    A scope may contain several contexts. It is possible that there is never a context (the function is never called); It may have, but now the context is destroyed after the function is called; It is possible to have one or more at the same time. In the same scope, different calls will have different execution contexts, resulting in different variable values.

What is a closure?

  • Quoting ruan Yifeng’s definition: a closure is a function that can read variables inside other functions

  • My brief summary: There are references to variables and methods outside a function or its child functions, so that the execution context created when the function is called cannot be destroyed automatically. Allows variables within a function to remain in memory and to be retrieved or changed from outside the function or its children. Closures, as I understand them, are just such a mechanism, more comprehensible than a concrete function

    Description:

    • Function external referenceThe parent function contains child functions, and the child function returns. The parent function keeps a reference relationship between the outside and the inside
    • Subfunction reference: There are asynchronous operation child functions in the parent function, such as event listener, setTimeout, etc., which reference the variables of the parent function. The execution environment of the parent function will also exist until the execution of the child function is completed
  • My detailed explanation:

    1. To understand closures, you need to understand the special scoping mechanism of JS. In the current scope (function scope and es6’s block-level scope), if no variable is in use, references are looked up through JS’s scope chain, up to the global scope of the top window. Conversely, the current scope cannot look down variables in its subscope.

    2. Then understand the execution context, which is created each time the function is called. After execution, the execution environment is automatically destroyed if other code no longer refers to a variable in the method.

    3. In its most common form, closures define child functions and variables (including parameters that are also variables in the parent function scope). Sub function return when we go out, when the parent function in the outside is called, sub function is assigned to a variable, the parent function outside is like the child inside a function is the relationship between the reference, will cause the child function execution context environment (i.e., the enforcement of its parent function is created when the execution context) will not be destroyed, but always exist in memory. In addition, variables (parameters) used in subfunctions that do not exist in the current subfunction are searched for references one level up through the scope chain and can be modified.

    4. A variable (parameter) used in a subfunction that does not exist in the current subfunction is referenced one level up through the scope chain. Outside of the parent function, these variables can be read and modified by child functions

Examples of closures:

Function father() {var father() = 1; N function son() {son() n++ = son(); son() n++ = son(); Log (n) to the global scope of the outermost window; } return son; } var result = father(); // son is referenced outside father, resulting in the execution context of son() being kept in memory as result(); // when result() is called for the first time, the operation is on the parent scope of n. The parent execution context is always in memory, so n is always result(); // when result() is called the second time, the operation is still on n in the parent scopeCopy the code

Closure purposes

This question has bothered me for a long time. I feel like I’ve never written code with closures, and I don’t think I’ve ever had to intentionally keep an execution context intact. I read a lot of blogs about it

  1. The for loop

SetTimeout = 4 setTimeout = 4 setTimeout = 4 setTimeout = 4

    for (var i = 0; i <= 3; i++) {
      setTimeout(function () {
        console.log( i)
      }, 3000)
    }
Copy the code

Since var has no block-level scope, this is equivalent to defining I in the global scope.

    var i = 0
    for (i; i <= 3; i++) {
      setTimeout(function () {
        console.log(i)
      }, 3000)
    }
Copy the code

SetTimeout in each loop refers to I in the global scope. SetTimeout is an asynchronous operation, so it will be executed after the for loop ends, at which point I will already be 4, so it will print all 4’s.

How do you solve this problem with closures?

The solution is to have the setTimeout jacket take an immediate function and pass in I as an argument to the immediate function’s scope. Each time the immediate function in the for loop executes, a separate execution environment is generated, and the I in each execution environment exists independently. In this case, the immediate execution of the function is equivalent to the parent function, and setTimeout is equivalent to the child function. Although it is not common to return the child function and keep the reference form, setTimeout is an asynchronous method that refers to I in the parent scope. Before the execution of setTimeout is complete, The immediate function creation execution environment will always exist.

    for (var i = 0; i <= 3; i++) {
      (function (n) {
        setTimeout(function () {
          console.log(n)
        }, 3000)
      })(i)
    }
Copy the code
  1. Encapsulate private variables (copied from others)

We can think of a function as a scope. The variables inside the function are private and cannot be referenced externally, but we can access private variables through the closure’s characteristics. At the same time, doing so does not pollute global variables

Var person = function(){var name = "default"; return { getName : function(){ return name; }, setName : function(newName){ name = newName; }}} (); console.log(person.name); // Use undefined console.log(person.getName()); // default person.setName("abruzzi"); console.log(person.getName()); // abruzziCopy the code
  1. Store variables that are not destroyed as long as references are kept

The closure problem

Since the execution context is not destroyed, memory leaks will occur. You can manually set the reference variable to NULL to free memory

The < body > < div desc = "div1" > block 1 < / div > < div desc = "div2" > block 2 < / div > < / body > < script > let divs = document. QuerySelectorAll (" div ");  divs.forEach(function(item) { item.addEventListener("click", function() { console.log(item.getAttribute("desc")); }); }); </script>Copy the code

As shown in the previous example, item is referenced, so it will not be destroyed. Instead, we only need the desc attribute of the node. We can store desc alone, so that item can be empty and unneeded data can be cleared to solve the memory leak problem

    let divs = document.querySelectorAll("div");
    divs.forEach(function(item) {
      let desc = item.getAttribute("desc");
      item.addEventListener("click", function() {
        console.log(desc);
      });
      item = null;
    });
Copy the code

This problem with closures

This refers to the object of the current function call, not the object where the position is defined. In the following example, fn() is called in the global scope, so this refers to the window

Var person = {name: 'KK', getName: function () { return function () { console.log(this.name) // undefined console.log(this) // window } } } var fn = person.getName() fn()Copy the code

The workaround is that the arrow function changes the this point

Var person = {name: 'KK', getName: Function () {return () => {console.log(this.name) // KK console.log(this) // {name: "KK", getName: ƒ}}}} var fn = person.getName() fn()Copy the code

The last

Closure problem, let’s stop here, if you don’t understand, recommend a site B video, I think it is good, this article also cited some examples in the video

This time I’m going to make JS closures very clear for you