It’s been a while. 55. Return of missing persons

preface

When I was in an interview two years ago, the interviewer at my current company asked me the basic question, what is a closure?

Me: Answer a function defined inside a function that can have variable access to the scope of the enclosing function.

Interviewer: After a function is removed from the stack, its function and temporary variables applied below it are destroyed. Why is it still accessible?

Me: Because the closure has persistent references to variables in its scope, the garbage collector reference has not been cleared yet and has not been found even though it is out of the stack.

Interviewer: How does the closure find variables in the scope of the outer function?

Me: Through the scope chain

Interviewer: Why don’t you talk about scope chains?

Me (Inner OS) : That’s it, I might not be able to do that well

Me: Uh, by uh, I forgot

Interviewer: Does the scope of an external function remain after it is removed from the stack?

Me: Well, it should be

Interviewer: Why?

Me: Oh, I can’t speak well here, I’ll go down to review (inner OS: cover your face, go down must have a good look here)

Finally, I had, but still didn’t review well under the, business line also cache for closure of scenes with relatively little (over your face, the rhythm of the lines of business and composition are not standard, don’t windbag, DDDD), it recently, our side of the project finally to encapsulate a maps API, I’m just a little review under the closure, The core of JS: closures, function scopes, function scope chains, and often flummoxed execution context, execution context stack, lexical environment, variable environment, exactly what, where.

closure

What is it

A combination of a function bound to (or surrounded by) references to its surrounding state (lexical environment) is a closure.

Why Use it

Advantages:

1. Make it possible for external access to internal variables of functions;

2. Local variables are resident in memory and can be used for caching.

3. Avoid using global variables to prevent pollution of global variables;

Disadvantages:

What is a closure? What are the pros and cons of closures? To delete (cut)

1. The referenced variables may have changed

function outer() { var result = []; For (var I = 0; i<10; I ++) {result.[I] = function () {console.info(I)}} return result}Copy the code

Solution: Use self-executing or let

function outer() { var result = []; for (var i = 0; i<10; i++){ result[i] = function (num) { return function() { console.info(num); Num = num; num = num; }}(I)} return result} // let function outer() {var result = []; For (let I = 0; i<10; I ++) {result[I] = function () {console.info(I)} return result}Copy the code

2. This points to the problem

var object = { name: 'I am, therefore I am 25. Therefore, I am 25. Therefore, I am 25Copy the code

Solution: Don’t play this way, use function modularity or class

function SomeObject() {
    var name = "object";
    this.getName = function () {
        console.log(name);
    }
}
new SomeObject().getName()
Copy the code

3. Memory leaks

Function showId() {var el = document.getelementById ("app") el.onclick = function(){aler(el.id) When showId is executed, Function showId() {var el = document.getelementbyid ("app") var id = el.id el.onclick = function(){ Aler (id) // This will cause the closure to reference the outer el. When showId is executed, the EL cannot be freed.Copy the code

Start with the JavaScript execution context

The following is excerpted from an in-depth understanding of the JavaScript execution context and execution stack

You’ve seen a lot of keywords in the introduction and in the previous section, what lexical context, stack, global context, function context, function scope, function scope chain, what, start muddling? What, why so many, ok, let’s slowly start from scratch, gradually eat these concepts.

Specification of the distinction

In ES3, the classic of classics, redbook 3, execution contexts include scope, variable Object, and this value.

In official ES5, the execution context includes: lexical environment, variable environment, and this value.

So, when you see a variable object, the scope is ES3, and if you see a lexical environment, the variable environment is ES5.

Execution context

An execution context is an abstraction of the context in which JavaScript code is evaluated and executed. Whenever Javascript code is running, it is running in an execution context.

There are three kinds:

1. Global execution context

2. Function execution context

3. Eval function execution context

Execution stack

What is it

A data structure with LIFO (Last in, first out) is used to store all execution contexts created while the code is running

When executing code, the JS engine creates a global execution context and pushes it onto the execution stack. Whenever it encounters a function execution, it creates a function context and pushes it onto the stack.

Create execution context

1. Create

  1. The determination of the this value, known as the This binding.

  2. Create the lexical environment (scope) ** component.

  3. Create the ** variable environment (variable object) ** component.

So the execution context is conceptually expressed as follows:

GlobalExectionContext = {LexicalEnvironment: {EnvironmentRecord: {Type: "Object", // bind identifier here} outer: <null> }}FunctionExectionContext = { LexicalEnvironment: { EnvironmentRecord: { Type: } outer: <Global or outer function environment reference>}}Copy the code

Lexical environment

It is a canonical type that defines identifiers and associations with specific variables and functions based on the lexical nesting structure of ECMAScript code. A lexical environment consists of an environment logger and a possible null value that references the outer lexical environment.

There are two types of lexical environments:

  • A global environment (in the context of global execution) is a lexical environment that has no references to the external environment. The external reference to the global environment is NULL. It has built-in Object/Array/ and so on, prototype functions in the environment logger (associated with global objects, such as the window Object) and any user-defined global variables, and the value of this refers to the global Object.

  • In a function environment, user-defined variables within a function are stored in the environment logger. And the referenced external environment may be the global environment, or any external function that contains this internal function.

Inside the lexical environment there are two components :(1) the environment logger and (2) a reference to the external environment.

  1. The environment logger is the actual location where variable and function declarations are stored.

  2. A reference to an external environment means that it has access to its parent lexical environment (scope).

Note – For function environments, the declarative environment logger also contains a arguments object passed to the function (which stores the index and parameter mapping) and the length of the argument passed to the function.

The variable environment

It is also a special lexical environment whose environment logger holds bindings created in the execution context of variable declaration statements.

As mentioned above, the variable environment is also a lexical environment, so it has all the attributes of the lexical environment defined above.

In ES6, one difference between a lexical environment component and a variable environment is that the former is used to store function declarations and variable (let and const) bindings, while the latter is used only to store var variable bindings.

Look at the sample code to understand the above concepts:

let a = 20; const b = 30; var c; function multiply(e, f) { var g = 20; return e * f * g; }c = multiply(20, 30);Copy the code

The execution context looks like this:

GlobalExectionContext = { ThisBinding: <Global Object>, LexicalEnvironment: { EnvironmentRecord: { Type: A: < uninitialized >, b: < uninitialized >, multiply: < func >} outer: <null>}, VariableEnvironment: {EnvironmentRecord: {Type: "Object", // bind identifier c: undefined,} outer: <null> }}FunctionExectionContext = { ThisBinding: <Global Object>, LexicalEnvironment: { EnvironmentRecord: { Type: "Declarative", // bindings identifiers: {0:20, 1:30, length: 2},}, outer: <GlobalLexicalEnvironment>},VariableEnvironment: {EnvironmentRecord: {Type: "Declarative", undefined }, outer: <GlobalLexicalEnvironment> }}Copy the code

Note – The function execution context is created only if the multiply function is called.

You may have noticed that the let and const variables are not associated with any value, but the var variable is set to undefined.

This is because during the creation phase, the engine examines the code for variable and function declarations, which are stored entirely in the environment but are initially set to undefined (in the case of var) or uninitialized (in the case of let and const).

This is why you can access variables defined by var (albeit undefined) before declaration, but access let and const variables before declaration will get a reference error.

This is what we call variable declaration enhancement.

2. Perform

** At this stage, all of these variables are allocated and the code is executed. 支那

The scope chain

It’s a lot, but it’s clear. Slow down.

So the scope chain is pretty simple here,

The scope chain is already created when the function is defined, and we can open the console to explore the actual JS engine by executing the following code.

function foo() {
    var a = 1;
    return function() {
        console.log(a)
    }
}
var a = foo()
a()
console.dir(a)
Copy the code

The printed A is as follows

ƒ anonymous() constructor: ƒ}__proto__: ƒ ()[[FunctionLocation]]: vm328:3 [[Scopes]]: Scopes[2]0: Closure (foo) {a: 1}1: Global {parent: Window, opener: null, top: Window, length: 0, frameCopy the code

You’ve found a magic item: [[Scopes]]

Yeah, it’s the mystery of what? I believe that after reading so much, you can say it yourself (old Riddler)

Scope chain? Function context?

If you click on Global you can see a lot of things that aren’t expanded here. (Actually I didn’t finish it either)

Believe you also see the CLosure of the east, let us more of a, below is the stack after are also live well has been printed in can take to the value of a, this time, the CLosure is to let him live, the principle of practical, from the scope chain is found under the scope to its still alive (cited) variables. (Because the closure allows anonymous access to A returned internally, a is not flushed from memory, so a is cached!)

In short, as the engine spins, the chain traces its actual scope and then goes to fetch the corresponding variable below. The closure is complete.

— — — — — — — — — — — — — — — — — — —

Believe in here, dear friend for closure in the js, scope, the understanding of the scope chain is a series of series connection, then on this basis, believe in its application, whether we can continue to point dry goods, such as in the practical work often use data cache scenarios, modular data (Model encapsulation of MVVM) scenario, good, Let’s move on:

Implementation of data caching…

Model layer abstraction in MVVM

How do I write my own MapAPI through closures

. Well, no! Look at this skull still numb! These a few first put a smoke bomb, after I update again (probably * may * forced true) out of hahahahahaha

reference

(ES5 version) In-depth understanding of JavaScript execution context and execution stack

What is a closure? What are the pros and cons of closures?