The introduction

In JavaScript, there are scopes, scope chains, and closures. We may start out thinking we know what these are (and I did when I was just getting started), but as we dig deeper, we realize that we only scratch the surface. So, this article will go into more detail about scopes, scope chains, and closures.

Let’s use a problem to understand the formation process of scope, scope chain and closure

let x = 1;
function A(y){
   let x = 2;
   function B(z){
       console.log(x+y+z);
   }
   return B;
}
let C = A(2);
C(3);
Copy the code

For the above solution diagram, there are the following explanations:

  • When a function is created, a heap is created and the current function Scope is initialized. The Scope ([[Scope]]) is the variable object VO/AO in its context.

  • When a function is executed, a new execution context is created -> initialize this to -> initialize the ScopeChain ([[ScopeChain]]) -> create the AO variable object store variable

  • In the context of EC(A) execution, A closure is formed when the heap is occupied by the global variable C and cannot be destroyed out of the stack.

  • The red line in the diagram represents the scope chain. In a scope, the variables it needs are not in the current scope and will be looked up level by level.

This simple question introduces the concepts of scope, scope chain, and closure, which will be formally explained in this article.

First, scope

Scope ([[Scope]]) is the accessible Scope of variables and functions, that is, Scope controls the visibility and life cycle of variables and functions.

1.1 Lexical scope and dynamic scope

1.1.1 Lexical scope

Lexical scope is also static, and is used in JavaScript. Lexical scope is the scope of the definition at the lexical stage. In other words, the lexical scope is determined by where you write the variable and block scope when you write the code. So the lexical parser keeps its scope constant when it processes code (most of the time)

Here’s an example to help you understand:

var value = 1;

function foo() {
    console.log(value);
}

function bar() {
    var value = 2;
    foo();
}

bar();
Copy the code

If the above example uses lexical scope, it would be executed as follows:

The bar() function is executed first, and the foo() function is executed in the bar() function, which outputs the value of value. It first looks to see if there is a value in the current scope, and if there isn’t, it looks up one level, and finally prints 1

1.1.2 Dynamic scope

Dynamic scope is the opposite of lexical scope. Let’s use the above example:

Suppose the above example uses dynamic scope:

It still executes the function as if it were lexically scoped, except that when foo() is executed, it does not look up the value one level, but from the scope of the calling function, so the final output is 2.

1.2 Global scope

Objects that can be accessed anywhere in your code have a global scope. In general, there are three ways to have a global scope

1.2.1 Outermost functions and variables defined outside the outermost functions

var globalValue = `global value`;

function checkGlobal() {
    var localValue = `local value`;
    console.log(localValue);                // "local value"
    console.log(globalValue);               // "global value"
}

console.log(globalValue);                   // "global value"
console.log(checkGlobal);                   // "global value"
console.log(localValue);                    // "Uncaught ReferenceError: localValue is not defined"
Copy the code

In the example above, globalValue is a global variable that can be accessed anywhere, while localValue is a local variable that can only be accessed inside a function

1.2.2 All variables not directly assigned by definition

function checkGlobal() {
    var localValue = 'local value';
    globalValue = 'global value';
    console.log(localValue, globalValue);    // 'local value' 'globalValue'
}

console.log(globalValue);                    // 'globalValue'
console.log(localValue);                     // "Uncaught ReferenceError: localValue is not defined"
Copy the code

1.2.3 Properties of all Window objects

1.3 Function scope

A function scope is a variable declared inside a function, which is the opposite of a global scope. The inner scope can access the outer scope, but the outer scope cannot access the inner scope.

function check() {
    var localValue = 'local value';
    console.log(localValue);          // 'local value'
}
 
console.log(localValue);              // "Uncaught ReferenceError: localValue is not defined"
Copy the code

1.4 block-level scope

Block-level scopes can be declared by lets and const, and the declared variables specify that they are not accessible outside the block-level scope.

1.4.1 When block-level scopes are created

  • Inside a function

  • Inside a code block

1.4.2 Characteristics of block-level scope

  • Declared variables are not promoted to the top of the code block

Variables declared by let or const are not promoted to the top of the current scope.

function check(bool) {
    if(bool) {
        let result = 1;
        console.log(result);
    }
    console.log(result);
}

check(true);   // 1 Uncaught ReferenceError: result is not defined
check(false);  // Uncaught ReferenceError: result is not defined
Copy the code

If you want to access result, you need to manually promote the variable to the top of the current scope yourself. Like this,

function check(bool) {
    let result = null;
    if(bool){ result = 1 } console.log(result); }...Copy the code
  • No duplicate declaration

Variables that have already been declared in the same level of scope cannot be declared again.

// 1true;
let bool = false;    // Uncaught SyntaxError: Identifier 'bool'Has already been declared // Var bool =true;
function check() {
    let bool = false; // no errors here //..... }Copy the code
  • Use in loops

Variables declared in a for loop are used only inside the loop.

for(let i = 0; i < 1; i++) {
    console.log(i);    // 0
}
console.log(i);        // Uncaught ReferenceError: i is not defined
Copy the code

But inside the loop is a separate scope

for(let i = 0; i < 2; i++) {
    let i = 'hello';
    console.log(i);    // 'hello' 'hello' 
}
Copy the code

Scope chain

2.1 Definition and formation of scope chain

When the desired variable cannot be found in its scope, it looks up layer by layer until it finds the global scope and gives up the search. This layer by layer relationship is the scope chain.

var a = 1;

function check() {
    return function() { console.log(a); Log (b); // If a is not found in the current scope, a is found in the current scope. // Same thing, so output"Uncaught ReferenceError: b is not defined"} } var func = check(); // return the anonymous function func(); // Execute an anonymous functionCopy the code

Third, the closure

3.1 Definition of closures

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

3.2 Formation of closures

Let’s look at a piece of code

function foo() {
    var a = 1;
    return function() {
        console.log(a);
    }
}

var bar = foo();
bar();
Copy the code

The result of the foo() function is returned to bar, which is not destroyed because the variable a is still in use, and then the bar() function is executed. In this way, we can access variables in the function’s inner scope from the outer scope. This is the closure.

Closure formation conditions:

  • Nested function

  • An inner function refers to a local variable of an outer function

3.3 The role of closures

  • You can read variables inside a function

  • The value of a variable can be kept in memory for a long time with a long life cycle.

  • Can be used to implement JS modules (JQuery libraries, etc.)

JS module is a JS file with specific functions, all the data and functions are encapsulated in a function inside (private), only to expose an object or function containing multiple methods to the outside, the user of the module, only through the object exposed by the module to call the method to achieve the corresponding function.

(function() {
    var a = 1;
    
    function test() {
        return a;
    }
    
    window.module = {a, test}; // exposed})()Copy the code
<! DOCTYPE html> <html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
    <script src="./1.js"> </script>
    <title>Document</title>
</head>
<body>
    <script>
        console.log(module.a);          // 1
        console.log(module.test());     // 1
    </script>
</body>
</html>
Copy the code

3.4 Features of closures

  • Every function is a closure, and a function can remember the scope in which it was defined, and where the function goes, the scope goes when it was defined.

  • A memory leak

A memory leak is an object that exists when you don’t need it. So you can’t abuse closures. When we are done with the closure, we should set the reference variable to NULL.

function outer(){
    var num = 0;
    return function add(){ num++; console.log(num); }; } var func1 = outer(); func1(); // 1 func1(); Var func2 = outer(); var func2 = outer(); func2(); // 1 [rereference function when closure is new] func2(); / / 2Copy the code

3.5 Application of closures

Now we need to implement the output of the number of button clicks

Let’s write an HTML

<button>1</button>
<button>2</button>
Copy the code

To write JS

let buttons = document.getElementsByTagName('button');

for(var i = 0; i < buttons.length; i++) {
    buttons[i].onclick = function() { console.log(i + 1); }}Copy the code

The first time we might write code like this, but we’ll see that there’s a problem with this code, that no matter how many buttons I click, it’ll print 3. This is because I is the global variable, and by the time the click event is executed, I has changed to 3

We can solve this problem in two ways

  • letThe statement

Change the var in the above code to let

  • closure
for(var i = 0; i < buttons.length; i++) {
    (function(k){
        buttons[k].onclick = function() {
            console.log(k + 1);
        }
    })(i)
}
Copy the code

The interview questions

let x = 5;
function fn(x) {
    return function(y) { console.log(y + (++x)); }}let f = fn(6);
f(7);   
console.log(x);
Copy the code

conclusion

This article is about scopes, scope chains, and closures. If you think it’s helpful, please give this article a thumb-up. If there’s anything wrong, please point it out

Finally, share my public number “Web front-end diary”, welcome to come to pay attention to ~