preface

JS closures, for every front end is an inconceivable concept. When I first started learning about closures, I spent a lot of time and effort understanding this concept. So here, I plan to write an article to share my learning experience and the closure in my eyes.

What is a closure

Let’s take a look at baidu Encyclopedia’s definition of closures:

Closures are functions that can read variables inside other functions. In javascript, for example, only subfunctions inside a function can read local variables, so closures can be understood as “functions defined inside a function.” In essence, closures are the bridge that connects the inside of a function to the outside.

I mentioned local variables, so what are local variables? Before we can understand local variables, we need to know what the process of execution of a function looks like.

Execution context

Execution context is one of the most important concepts in JavaScript. The execution context defines the other data that a variable or function has access to and determines their respective behavior. Each execution context has a variable object associated with it.

The global execution context is the most peripheral execution context. This context varies depending on the hosting environment in which ECMAScript resides. In the browser, the global execution context is a Windows object. Variables declared in the global environment are global variables. All global variables and functions are created as properties and methods of the Window object. After all the code in an execution context has been executed, the context is destroyed, along with all variables and functions stored in it (the global execution context is not destroyed until the application exits — such as when the browser or web page is closed).

Each function also has its own execution context, and when the flow of execution enters a function, the function’s environment is pushed into an environment stack. After the function completes execution, the stack pops its environment, returning control to the previous execution environment.

Note: The execution context, as the name implies, is defined when a piece of code is executed, so it is dynamic. When a function is called in different environments, it may result in different execution contexts.

Let’s look at an example:

Var a = "global environment "; Function A() {var A = "local environment "; B(); } function B() { console.log(a); } A();Copy the code

Let’s simulate the process when the browser executes the above code.

  1. Execution stack, which exists in the global execution contextGlobal variable AThe sum of function A and function B.

  1. The execution stream enters the A function atExecution stackPushes the execution context of function A, which existsLocal variable A.

  1. Execute function B call instruction in function A code block,Execution stackPushes the execution context of function B, which printsVariable a.

At this point, the console prints outThe global variable.

Function B is supposed to find the execution context of function A and print out local variables, but why is it supposed to print out global variables instead?

And that’s where it leadsscopeThe concept of

scope

Scope: Refers to the scope of a variable. Before ES6, JS had only global scopes and function scopes. In ES6, block-level scopes were introduced (this article will not discuss block-level scopes for the time being).

If a variable is defined globally, it exists in the global scope. In the same way, variables defined inside a function exist in the scope of the function.

Variables declared in their respective scopes are valid only in their respective scopes.

The scope chain

When a JS function is declared, it creates a chain of scopes. Its purpose is to guarantee ordered access to all variables and functions that the execution context has access to. The front end of the scope chain is always a variable object in the context of the currently executing code. If the context is a function, its variable objects are the variables declared inside it and the arguments object that entered the arguments. The next variable in the scope chain comes from the containing (external) context, and so on down to the global execution context. While the JS function is declared, the lexical scope is adopted, that is, the scope chain is determined when the declaration is made. The definition of a scope chain is static!

In the example above, functions A and B are defined with only the global execution context in their external execution context, so their scope chains are:

Function A/B scope -> global scope.

So, in the example above, function B internally looks for its internal variable object through its scope chain, finds no variable a, goes up through the scope chain to find the global variable in the global scope, and finally prints out the result.

Personal understanding: the execution stack formed by the execution context, all variables or functions are merged. However, not all variables or functions can be retrieved from the execution context at the top of the stack, because the existence of scope, each scope, only through the scope chain to access the correct variables or functions. The execution context is dynamic, with different execution stacks depending on the context of the calling function. The scope chain is static. It is generated at the beginning of the creation of each function according to the lexical scope. It specifies which variables or functions are allowed to access in the execution stack.

So, how do I get a value in a function scope at global scope?

closure

Having said all that, the closure is finally here. Closures are functions that have access to variables in the scope of another function. Closures are created by returning an anonymous function inside a function. Let’s modify the example above:

Var a = "global "; Function A() {var A = "local environment "; return { B: function () { console.log(a); }}; } var obj = A(); obj.B(); // "local environment"Copy the code

Now, we return an object inside function A that has A function B inside it. We call the function B in the global environment and print out the local environment. This is A closure where we successfully call A variable in the scope of function A in the global scope. What’s going on? Let’s execute this code again:

  1. The global execution context is pushed into the execution stack, where there is a global variable a, a function a, and an object obj.

  1. Function A is called, and the execution context of function A is pushed into the execution stack. There is a local variable a in this context.

  1. performobj.B(), pushes the function B execution context onto the execution stack.

  1. Print the variable a according toLexical scopeSo let’s drawThe scope chain. Function A is declared globally, so itsThe scope chainThe next section points to the global scope, and function B is declared in function A, so itsThe scope chainThe next part of A points to the scope of function A.

At this point, the scope chain of function B points to the scope of function A, so the local variable is printed. Now we can use function scoped variables in the global environment through closures. Isn’t that nice?

Abstract points to understand closures: When a function returns an anonymous function inside, the anonymous function carries a backpack (the variable object of the external function referenced through the scope chain) in addition to its own items (the internal variable object). Variables inside the function can be accessed from outside the function through the backpack.

Note: Because the anonymous function returned by the closure has a reference to its external function variable object, the original external function variable object will be reclaimed, but because of the existence of the closure, the external function variable object is still referred to, so the memory will not be reclaimed. As we use closures, we need to be aware of their side effects.

conclusion

The concept of closures is not difficult to understand, as long as you understand the implementation context, scope, chain of scope, etc., you can understand the concept of closures. The use of closures in JS is very extensive, understand the closure, I believe your JS foundation will be more solid. As I am also in the learning stage, if there are any mistakes in the above article, please feel free to point out in the comments section!

Finally, the code word is not easy, if you think I write good, please point the valuable praise, your support is the biggest motivation for my creation. QAQ