Scope is a very important and fundamental concept in JavaScript. Many people think they understand scopes, but can’t say why or even identify them when they come across closures.

Closures are also a very important and often misunderstood concept. Closures, however, are a natural consequence of scope-based code. If you talk about closures without scope, you’re playing hoodlum. Closures are ubiquitous in everyday code, but what really makes closures useful is the isolation of scopes, module functions, and so on.

The scoping mechanism is not directly visible, so we first simulate a scenario to illustrate the scoping rules as best we can, and then verify them with code snippets and developer tools.

The game file

Everyone must have played the game experience. At the beginning, the first level, the difficulty is relatively easy. In the second level, the difficulty increased accordingly by adding some difficult characters to the first level. The further the level goes, the more difficult the characters become.

But in the game, due to various reasons, it is often impossible for us to pass all the levels at once, so the game provides the function of saving. Next time you play it, you can renew it from the save. If you don’t want to, you can start all over again.

Why can we jump straight from the archive to the last level? Obviously, there is a record store here. For example, the first level has a scene of cannibal flowers and Aquaman, and the second level has an evil man and so on. Each level records new characters or scenes added to that level as well as records of previous levels. This ensures that the different saves are kept separate, and that no matter which level is saved, it will be saved in the same place the next time. Of course, we could go back to the previous level.

The Power of the Sea King & the evil of the enemy

A few points

With the above scenario, let’s go back to the following knowledge points.

  1. Identifier: The name of a variable, function, attribute, or function argument.

  2. Each function has its own execution environment. When the execution stream enters a function, the function’s environment is pushed onto an environment stack. After the function is executed, the stack ejects its environment, returning control to the previous execution environment.

  3. The execution environment defines other data that a variable or function has access to. Each execution environment has a variable object associated with it, and all variables and functions defined in the environment are stored in this object. After all code in an execution environment is executed, the environment is destroyed, along with all variables and function definitions stored in it.

  4. When code is executed in an environment, a scope chain of variable objects is created.

  5. A scope chain is an ordered access to all variables and functions that the execution environment has access to. The front end of a scope is always the variable object of the currently executing code. If the environment is a function, treat its active object as a variable object. The active object initially contains only one variable, the Arguments object. The next variable object in the scope chain comes from the containing (external) environment. The variable object of the global execution environment is always the last object in the scope chain.

  6. When an identifier is introduced in an environment for reading or writing, it must be searched to determine what the identifier actually represents. The search process starts at the front of the scope chain and works its way up the list of identifiers that match a given name. If the identifier is found in the local environment, the search process stops and the variable is ready. If the variable name is not found in the local environment, the search continues up the scope chain. The search will trace all the way back to variable objects in the global environment. If this identifier is not found in the global environment either, it means that the variable has not been declared.

  7. A scope chain is essentially a list of Pointers to variable objects that reference but actually contain no variable objects.

If we put the above points together, this is called the scope chain rule. Arguments should be added to variable objects.

Graphic scope

Now let’s start with the last two lines,

var outer = outerFn(10);
var inner = outer(10);
Copy the code

Outer = outerFn(10), outer has a reference to the return function. When outer(10) executes, it creates its own chain of scopes containing variable objects from the function’s external environment.

When reading initial, which is not retrieved in the Inner variable object, it searches up the scope chain and finds the identifier in the outer variable object. The search stops and the variable is ready.

When a function is defined, it determines what its scope will contain when it executes. This also explains that we can access variables inside a function even if we throw it outside. It doesn’t matter where the inner function is executed.

Why emphasize its own?

function outer() {
    var num = 0;
    return function inner() {
        returnnum++; }}let innerFn_1 = outer();
let a_1 = innerFn_1()
let innerFn_2 = outer();
let a_2 = innerFn_2();

let a_1_1 = innerFn_1();
let a_2_2 = innerFn_2();
Copy the code

InnerFn_1 and innerFn_2 belong to their own scoped chains, while A_1 and A_2 create their own scoped chains on innerFn_1 and innerFn_2, respectively. So num in their functions is a variable in a different scope chain. But a_1 and a_1_1 are both based on innerFn_1, have the same outer variable object, and num is the same, so they add up. Same thing with a_2 and a_2_2.

If you understand this, a common interview question is a piece of cake.

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

The point is that a scope chain of variable objects is created at execution time.

What is a closure?

Closures occur when a function can remember and access its scope, even if it is executed outside the current scope. This is similar to the game save mentioned earlier.

All right, let’s throw in some closures to solidify it.

function outer_1() {
    var a = 'hello world';
    function inner() {
        console.log(a)
    }
    outer_2(inner)
}
function outer_2(fn) {
    fn()
}
Copy the code

There are also closures here.

var a = new array(99999999);
function b() {
    console.log(b)
}
b()
window.addEventListener('click'.function() {
    console.log('hello world')})Copy the code

Look at closures visually in DevTools

Also mentioned at the beginning of the developer tools can be combined with a visual look, a dynamic map to explain everything.

A memory leak

A closure is a closure because it records the scope of the function. The dominant automatic garbage collection mechanisms do not free memory because of this feature of closures. The misuse of closures can cause memory to have less space to allocate and eventually crash.

Normally, during the execution of a function, local variables are allocated memory to store their values until the end of the function. The space occupied by the local variable is then freed for future use.

One of the most commonly mentioned recycling mechanisms, tag cleanup, works by marking all variables stored in memory as they enter the execution environment (we don’t care what tags), then finding the variables in the environment and the variables referenced in the environment and removing their tags. The remaining marked variables are considered to be ready for deletion. Finally, the garbage collector finds variables that are no longer in use and frees up the memory they occupy. Therefore, once the data is no longer needed, it should be dereferenced and its value set to NULL.

outer = null;
inner = null;
Copy the code

The execution environment of the inner function holds a reference to the live object of the outer environment. When the inner function is thrown, it means that the variables in the outer environment cannot be destroyed.

This object

The execution environment records more than that, it also records the function call stack, how the function is called, etc. This is related to scope, but not in the way you might think. Each function automatically gets two special variables when called: this and arguments. The inner function searches for these two variables only up to their active object (the current variable object). So you can never directly access these two variables in an external function. Unless we store the this object in the outer scope in a variable accessible to the closure.

// very common is not 😂
let obj = {
    a: function() {
        var self = this;
        return function() {
            console.log(self)
        }
    }
}
Copy the code

The internal value of this is not formally assigned until the function is executed, so where the function is called is critical. This refers to whoever directly called this function, so to speak. If the object is not calling this function directly, we can assume that undefined. If the browser is not in strict mode, it is window. If you really want to know why, look directly at the specification.

'use strict'
function a() {
    console.log(this)}var b = {
    a: function() {
        console.log(this);
    },
    b: function() {
        returna; }}let b_a = b.a;
a();    //1. undefined;
b_a();  //2. undefined;
b.a();  //3. {a: f, b: f};
b.b()();    //4. undefined;
(true && b.a)() //5. undefined;
new a();    / / 6. {}
b.call(b);  //7. {a: f, b: f};
Copy the code

From 1 to 6, let’s see which object directly calls this function.

The first call object was not found and was a normal function call. The second assignment, b_a = b.a, returns the normal function, just a normal function call. The third one is pretty straightforward, which is the object B. The fourth is a closure. First, this will only look for this in the current active object. We don’t know which object it is, but it can’t be B. Number 5 is the same as number 2. Number six, it’s not really a function call, but we know that this refers to the newly created empty object. Number seven is more direct, people are called out by name.

The rules for this binding object seem to be the only one left in my list 😌.