preface

In my last article, I talked about what scope is, and how ES6 supports both variable promotion and block-level scope through both the lexical and lexical environments. Finally, I talked about how to find variables through both the lexical and the lexical environments, which involves the concept of scope chains.

Scope Chain

So what is a scope chain? As I understand it, a chain lookup is used to determine which data can be accessed by the inner function, based on the mechanism by which the inner function can access the variables of the outer function. This scope chain determines the order in which the code in each level of context accesses variables and functions. To understand how js chains lookup, you must first understand the js execution environment.

Execution Context

Each function call has its own context, and each context has an associated variable object, on which all variables and functions defined in that context exist.

The global context is the outermost execution context. Depending on the hosting environment implemented in ECMAScript, the objects representing the global context may be different. In a browser, the global context is what we call the window object, so all global variables and functions defined via var become properties and methods of the Window object. Top-level declarations that use let and const are not defined in the global context, but have the same effect on scope chain resolution.

Each function call has its own context. When code execution flows into a function, the context of the function is pushed onto a context stack. After the function executes, the context stack pops up the function context, returning control to the previous execution context. The flow of ECMAScript execution is controlled through this context stack.

Here’s an example:

var scope = 'global'; 
function fn1(){
   return scope; 
}
function fn2(){
   return scope;
}
fn1();
fn2();


Copy the code

The execution of the above code can be illustrated with the following figure.

Now that we know about environment variables, let’s talk more about scope chains.

Context code, when executed, creates a scope chain of variable objects and assigns the scope chain to a special internal property ([scope]). You then initialize the function’s activation Object using the values of this, Arguments (which is not available in the global context), and other named parameters. The variable object for the context in which the code is executing is always at the forefront of the scope chain.

If the context is a function, its activation Object is used as a variable object. The live object originally has only one definition variable :arguments. (This variable is not available in the global context.) The next variable object in the scope chain comes from the above containing context, and the next object comes from the next containing context. And so on down to the global context; The variable object of the global context is always the last variable object in the scope chain.

Identifier resolution at code execution is done by searching for identifier names down the scope chain. The search process always starts at the top of the scope chain, and then works its way up until the identifier is found. (If no identifier is found, an error is usually reported.)

Here’s an example:

var color = 'blue';
function changeColor() {
  if (color === 'blue') {
    color = 'red';
  } else {
    color = 'blue';
  }
}
changeColor();
Copy the code

For this example, the scope chain of the function changeColor() contains two objects: its own variable object (which defines the Arguments object) and a variable object for the global context. The variable color is accessible inside this function because it is found in the scope chain.

Closure (closure)

Now that we know the scope chain, we can talk about closures. Closures are functions that refer to variables in the scope of another function, usually implemented in nested functions. Closures do two things:

  • The first is that you can read variables outside of your own function (look along the scope chain)
  • The second is to keep these external variables in memory

This object

You often see a question like this about closures:

window.identity = 'The Window'; let object = { identity: 'My Object', getIdentityFunc() { return function() { return this.identity; }; }}; console.log(object.getIdentityFunc()()); // 'The Window'Copy the code

Using this in closures can complicate the code. If the internal function is not defined using the arrow function, the this object is bound at runtime to the context in which the function is executed. If called in a global function, this equals window in non-strict mode and undefined in strict mode. If called as a method on an object, this equals the object. Anonymous functions are not bound to an object in this case, which means that this points to window unless this is undefined in strict mode.