By: Small Potato Blog park: www.cnblogs.com/HouJiao/ Nuggets: juejin.cn/user/243617…

preface

Before we get started, let’s look at two snippets of JavaScript code.

A code

console.log(a);
var a = 10;

Copy the code

Code 2

fn1();
fn2();

function fn1(){
    console.log('fn1');
}
var fn2 = function(){
    console.log('fn2');
}
Copy the code

If you can correctly answer and interpret the output of the above code, you have some understanding of the context in which JavaScript is executed. On the other hand, after reading this article, I’m sure you’ll get the answer.

What is an execution context

var a = 10;

function fn1(){
    console.log(a);     / / 10
    function test(){
        console.log('test');
    }
}

fn1();
test();   // Uncaught ReferenceError: test is not defined
Copy the code

In the above code, we define variable A and function fn1 in the global environment. When calling function fn1, variable A defined in the global environment can be successfully accessed from inside FN1. Next, we call the test function defined inside Fn1 in the global environment. This line of code causes ReferenceError because we cannot access the test function inside Fn1 in the global environment. Whether these variables or functions can be accessed normally depends on the context in which the JavaScript is executed.

The execution context of JavaScript is also called the execution environment of JavaScript. It is created during the execution of JavaScript code. It defines the variables and functions that the current code can access, and also supports the operation of the entire JavaScript code.

During the execution of a piece of code, a global execution context is created if code in the global environment is executed, and a function execution context is created if a function is encountered.

As shown in the figure above, the code creates three execution contexts during execution: one global execution context and two function execution contexts. Because there is only one global environment, only one global execution context is created during code execution; Functions can be defined more than one, so it is possible to create multiple function execution contexts depending on the code.

JavaScript also creates an execution context stack to manage multiple execution contexts created during code execution.

The execution context stack can also be called the environment stack, which will be referred to simply as the execution stack in subsequent descriptions.

The execution stack and the stack in the data structure are the same data type, with the characteristics of first in, last out.

Create the execution context

Earlier, we briefly understood the concept of an execution context and learned that multiple execution contexts are managed through an execution stack. How the execution context records the variables and functions that are accessible to the current code is what we’ll need to discuss next.

First we need to clarify that the execution context lifecycle consists of two phases: the creation phase and the execution phase.

The create phase corresponds to our code, that is, the code has just entered the global environment or the function has just been called; In the execution phase, code is executed line by line.

Create a stage

The creation phase of the execution context does three things:

  1. createVariable Object (VO)
  2. createScope Chain
  3. determinethisPoint to the

What is a variable object and a scope chain?

Variable object: A variable object stores variables and functions accessible to the current environment in the form of key: value, where key is the name of a variable or function, and value is the value of a variable or function reference.

Scope chain: variable object scope chain is made of a list or list structure, the forefront of the scope chain is the variable object of the current environment, the scope of the next element is an environment variable on object, and then the next element is up an environment variable object, all the way to the global environment variables in the object; The variable object of the global environment is always the last object in the scope chain. When we access a variable or function in a piece of code, we look for it in the variable object of the execution context of the current environment, or if we don’t find it, we look down the scope chain.

The environment described here is no more than two kinds, one is the global environment, one is the environment of the function.

See Chapter 4, Section 2, JavaScript Advanced Programming, 3rd edition.

I’m sure a lot of people don’t have the confidence to look down at this point, because I’ve thrown out a lot of concepts: execution context, execution context stack, variable objects, scope chains, and so on. But it doesn’t matter, we don’t have to tangle too much with these so-called nouns, the above content can roughly have an impression, continue to read, doubts will be slowly solved.

Global execution context

Let’s take the global environment as an example and examine how the global execution context behaves during the creation phase.

In order to simulate the code entering the global environment, I set a breakpoint at the beginning of the JavaScript script.

<script>debugger
    var a = 10;
    var b = 5;
    function fn1(){ 
        console.log('fn1 go')}function fn2(){
        console.log('fn2 go')
    }
    fn1();
    fn2();
</script>
Copy the code

This kind of debugging may not be very accurate, but it can help us understand abstract concepts very well.

Run this code and it stops at the breakpoint. At this point we access the variables and functions we defined in the browser’s Console tool.

As you can see, we can already access the variable defined by var. This is called variable declaration promotion, but because the code has not been executed, the value of the variable is undefined. The declared function can also be called normally, which is called function declaration promotion.

We said that the variable object holds the variables and functions accessible to the current environment, so the contents of the variable object are roughly as follows:

// A variable objectVO: {a: undefined.b: undefined.fn1<Function fn2()> <Function fn2()>Copy the code

This already refers to the window object.

So this reads as follows:

// This holds the address of the window object, i.e. this refers to the window
this: <window Reference> 
Copy the code

Finally, there is the scope chain, the contents of which can be seen in the browser’s breakpoint debugging tool.

Expanding the Scope item, you can see that the current Scope chain has only one GLobal element and a window identifier to the right of GLobal, which indicates that the GLobal element refers to the Window object.

// Scope chain
scopeChain: [Global<window>].// The current scope chain has only one element
Copy the code

At this point, the variable object, scope chain, and this point in the global execution context during the creation phase are combed as follows:

// Global execution context
GlobalExecutionContext = {
    VO:{
    	a: undefined.b: undefined.fn1Fn2: <Function fn2()> // scopeChain: <Function fn1()> [Global<window>], // The scope chain in the Global environment has only one element, which is Global, and refers to the window object this: <window Reference> // this holds the address of the window object, that is, this refers to window}Copy the code

Previously, we said that the scope chain is composed of variable objects. The first part of the scope chain is the variable object of the current environment. So based on this concept, we should be able to deduce that: GlobalExecutionContext. VO = = Global < window > = = the result of the window to true, because GlobalExecutionContext. VO and Global < window > are we variables defined in pseudo code, It doesn’t exist in the actual code, and we don’t have access to the actual variable object, so let’s take a look at the breakpoint debugging tool in the browser.

Let’s expand the Global option.

You can see that in Global we define variables A and B and functions fn1 and fn2. At the same time, we often use variables such as document function alert, conform, etc., so we can say that Global refers to the window object, which can be displayed in the browser.

Finally, the corresponding execution stack:

/ / execution stack
ExecutionStack = [
    GlobalExecutionContext    // Global execution context
]
Copy the code

Function execution context

The breakpoint is set before the fn1 function executes, referring to the global context.

<script>
    var a = 10;
    var b = 5;
    function fn1(param1, param2){ debugger
        var result = param1 + param2;
        function inner() {
            return 'inner go';
        }
        inner();
        return 'fn1 go'
    }
    function fn2(){
        return 'fn2 go'
    }
    fn1(a,b);
    fn2();
</script>
Copy the code

Open the browser, pause the code execution at the breakpoint, and continue to access some related variables and functions in the Console tool.

According to the actual debugging results, the variable object of the function execution context is as follows:

In fact, in the article of function execution, the variable Object is not called the variable Object, but is called the Active Object (AO). In fact, they are just called the difference, so in the following pseudo-code, I unified write VO. But it’s important to make a statement here so as not to cause some misunderstanding.

// A variable object
VO: {
    param1: 10.param2: 5.result: undefined.inner: <Function inner()>, arguments:{0: 10, 1:5, length: 2, callee: <Function fn1()>}}Copy the code

Function execution context () ¶ The function execution context variable object includes the internal variables and functions defined by the function, as well as the arguments object.

Arguments objects are local variables in all (non-arrow) functions that correspond to function arguments. You can use arguments to get function arguments.

The scope chain of the function execution context is as follows:

In code:

// Scope chain
scopeChain: [
    Local<fn1>,     // The variable object of the execution context of the fn1 function, namely, fn1executionContext.vo
    Global<window>  / / global execution context variable object, namely GlobalExecutionContext. VO
] 
Copy the code

The first element in the scope chain is Local, which is the variable object of the current environment (fn1). We can expand Local, and its content is basically the same as the variable object VO summarized earlier.

The content of this Local expansion is basically the same as the previous summary of active object AO, here is only Chrome browser display, do not have too much trouble.

This object also refers to the window.

The this inside fn1 refers to the window object because of how fn1 is called.

To summarize the behavior of the function execution context during the creation phase:

// Function execution context
Fn1ExecutionContext = {
    VO: {
        param1: 10.param2: 5.result: undefined.inner: <Function inner()>, arguments:{0: 10, 1:5, length: 2, callee: <Function fn1()>}}, scopeChain: [Local<fn1>, // fn1executionContext. VO Global<window> Namely GlobalExecutionContext. VO], this: < window reference >}Copy the code

The execution stack is as follows:

/ / execution stack
ExecutionStack = [
    Fn1ExecutionContext,      // fn1 execution context
    GlobalExecutionContext    // Global execution context
]
Copy the code

Execution phase

The execution phase of the execution context, which is relatively simple, basically involves assigning values to variables and executing each line of code. Here, taking the global execution context as an example, the behavior of the execution context at the execution stage is sorted out:

// Function execution context
Fn1ExecutionContext = {
	VO: {
            param1: 10.param2: 5.result: 15.inner: <Function inner()>, arguments:{0: 10, 1:5, length: 2, callee: <Function fn1()>}}, scopeChain: [Local<fn1>, // fn1executionContext. VO Global<window> Namely GlobalExecutionContext. VO], this: < window reference >}Copy the code

Perform an extension of the context

For those of you who are watching this, I believe you have a little bit of an understanding of the context in which JavaScript is executed. I’ve omitted some special cases to give you a better understanding of JavaScript execution contexts, so let’s take a breather and look at more about execution contexts.

Let and const

Those familiar with ES6 will know that there are two new keywords that define variables in ES6: let and const, and there is no variable declaration enhancement for these two keywords.

Again with the previous series of debugging methods, let’s look at lets and const in the global environment. First let’s run the following JavaScript code.

<script> debugger
    let a = 0;
    const b = 1;
</script>
Copy the code

The breakpoint accesses variables A and B and detects an error.

We cannot access variables defined by let and const during the execution phase of the execution context. It also shows that there are no let, const variables in the variable object during the creation phase of the execution context.

function

Functions are generally defined in two ways, the first is a function declaration, and the second is a function expression.

// Function declaration
function fn1(){
    // do something
}

// Function expression
var fn2 = function(){
    // do something
}
Copy the code

Let’s run the following code.

<script> debugger
    function fn1(){
        return 'fn1 go';
    }

    var fn2 = function (){
        return 'fn2 go';
    }
</script>
Copy the code

The code pauses at the breakpoint and calls the functions fn1 and fn2 manually.

As can be seen from the results, for function declarations, functions can be used before function definitions because there is a function declaration promotion. In the case of a function expression, using it before the function definition results in an error, indicating that the function expression does not have a function declaration boost.

This example complements what has already been said: during the creation phase of the execution context, the contents of the variable object do not contain function expressions.

Lexical environment

In the process of sorting out this article, I see many articles mentioned the concept of lexical environment and variable environment, which is proposed by ES5 and is another design and implementation of variable objects and scope chains described above. Based on this new concept introduced in ES5, the corresponding execution context representation will also change.

// Execution context
ExecutionContext = {
    // lexical context
    LexicalEnvironment: {
        // Environment record
    	EnvironmentRecord: {},// External environment references
        outer<outer reference>}, // VariableEnvironment: {// EnvironmentRecord: {}, // <outer reference>}, // This to this: <this reference>}Copy the code

A lexical environment consists of an environment record, which, like a variable object, holds variables and functions in the current execution context, and an external environment reference. At the same time, environment records are called object environment records in the context of global execution and declarative environment records in the context of function execution.

// Global execution context
GlobalExecutionContext = {
    // lexical context
    LexicalEnvironment: {
        // Environment record object environment record
    	EnvironmentRecord: { 
            Type: "Object"    // type indicates that the environment record is an object environment record
        },
        // External environment references
        outer: <outer reference>}} // FunctionExecutionContext = {// LexicalEnvironment: {// EnvironmentRecord: {Type: 'Declarative' // Type identifier, indicating that the EnvironmentRecord is Declarative}, // environment reference outer: <outer reference> } }Copy the code

Just as variable objects exist only in the global context, they are called active objects in the functional context.

An external context in a lexical context holds the lexical context of other execution contexts, which is similar to a scope chain.

The variable environment only holds variables declared by var. Other variables such as let, const variables, function declarations, and arguments objects from functions are stored in the lexical environment.

Take this code as an example:

var a = 10;
var b = 5;
let m = 10;
function fn1(param1, param2){
    var result = param1 + param2;
    function inner() {
        return 'inner go';
    }
    inner();
    return 'fn1 go'
}
fn1(a,b);
Copy the code

If the execution context were represented in terms of the new concepts of lexical and variable environments mentioned in ES5, it would look like this:

/ / execution stack
ExecutionStack = [
    fn1ExecutionContext,  // fn1 execution context
    GlobalExecutionContext,  // Global execution context
]
// fn1 execution context
fn1ExecutionContext = {
    // lexical context
    LexicalEnvironment: {
        // Environment record
    	EnvironmentRecord: { 
            Type: 'Declarative'.// The environment record of a function is called declarative environment record
            arguments: {
                0: 10.1: 5.length: 2
            }, 
            innerOuter: <GlobalLexicalEnvironment>}, // {// EnvironmentRecord: {Type: 'Declarative', // EnvironmentRecord is called Declarative Var var var var var var var var var var var var var var var var var <GlobalLexicalEnvironment>}} // GlobalExecutionContext GlobalExecutionContext = {// LexicalEnvironment: {// EnvironmentRecord: {Type: 'Object', // the EnvironmentRecord of the global execution context is called the Object EnvironmentRecord m: < uninitialized >, fn1: <Function fn2>}, outer: <null>}, // VariableEnvironment: {// EnvironmentRecord: {Type: 'Object', // EnvironmentRecord: {Type: 'Object', // Outer: <null> outer: <null> outer: <null}Copy the code

Understand Javascript execution context and execution stack. There is not much research on lexical environments, so this article will not cover more, and the rest of the content will be based on variable objects and scope chains.

Debugging Method Description

The debugging methods in this article are just one of the ways I’ve practiced myself, such as pausing the code at breakpoints and then using the console tool to access variables or call functions that can be written into the code.

console.log(a);
fn1();
fn2();
var a = 10;
function fn1(){
    return 'fn1 go';
}
var fn2 = function (){
    return 'fn2 go';
}
Copy the code

The same conclusion can be drawn when a variable access error or a function call error occurs before the code is executed to a variable declaration or function declaration.

On the contrary, in the practice of my debugging method, there will be a lot of inconsistent phenomena, such as the following example.

Function declarations can be used before function definitions, but function expressions cannot. If I debug in this way, I will find that calling inner and other will be wrong.

If you have the same problem, don’t get too tangled up. Be sure to combine the actual code running results with the theoretical concepts in the book to properly understand the JavaScript execution context.

To practice practice

Ten years of work off the stage, and finally one minute on the stage. After understanding the execution context of JavaScript, some high-frequency interview questions and codes circulated on the Internet can be analyzed with the relevant knowledge of the execution context.

The first two pieces of code are posted at the beginning of this article.

A code

console.log(a);
var a = 10;
Copy the code

The result of this code is obvious: the result of console.log is undefined. The principle is simple: variable declaration enhancement.

Code 2

fn1();
fn2();

function fn1(){
    console.log('fn1');
}
var fn2 = function(){
    console.log('fn2');
}
Copy the code

This example should also be a piece of cake, as we have already done the code debugging: fn1 can be called normally, but calling fn2 causes ReferenceError.

Code 3

var numberArr = [];

for(var i = 0; i<5; i++){
    numberArr[i] = function(){
        return i;
    }
}
numberArr[0] (); numberArr[1] (); numberArr[2] (); numberArr[3] (); numberArr[4] ();Copy the code

This code if you brush the interview question must know the answer, then this time we use the knowledge of execution context to analyze it.

step 1

The code enters the global environment and begins the creation phase of the global execution context:

/ / execution stack
ExecutionStack = [
    GlobalExecutionContext    // Global execution context
]
// Global execution context
GlobalExecutionContext = {
    VO: {
    	numberArr: undefined.i: undefined,},scopeChain: [
    	Global<window>  / / global execution context variable object, namely GlobalExecutionContext. VO].this: <window reference>
}
Copy the code

step 2

The code is then executed line by line, beginning the execution phase of the global execution context.

As the code begins to enter the first loop:

/ / execution stack
ExecutionStack = [
    GlobalExecutionContext    // Global execution context
]
// Global execution context
GlobalExecutionContext = {
    VO: {
        // Number is an Array of length 1, and the first element is a Function
    	numberArr: Array[1][f()], 
        i: 0,},scopeChain: [
    	Global<window>  / / global execution context variable object, namely GlobalExecutionContext. VO].this: <window reference>
}
Copy the code

The execution context summary above is that the code has entered the first loop, skipping the declaration and assignment of numberArr, and all the subsequent code only analyzes the key parts, not line by line.

step 3

The code enters the fifth loop (the fifth loop does not actually execute because the condition is not met, but the I value has been incremented by 1) :

Omit the execution context contents of I =2, I = 3, and I = 4.

/ / execution stack
ExecutionStack = [
    GlobalExecutionContext    // Global execution context
]
// Global execution context
GlobalExecutionContext = {
    VO: {
        // Number is an Array of type 5, and each element is Function
    	numberArr: Array[5][f(), f(), f(), f(), f()],
        i: 5,},scopeChain: [
    	Global<window>  / / global execution context variable object, namely GlobalExecutionContext. VO].this: <window reference>
}
Copy the code

When we’re done with this part of the loop, we see that I is already 5.

step 4

We then access the elements in numberArr (each element in numberArr is an anonymous function that returns the value of I) and call. First, the element with subscript 0 is accessed, and then the corresponding anonymous function is called. Since this is a function call, it also generates a function execution context.

/ / execution stack
ExecutionStack = [
    FunctionExecutionContext   // Anonymous function execution context
    GlobalExecutionContext    // Global execution context
]
// Anonymous function execution context
FunctionExecutionContext = {
    VO: {},    // The variable object is empty
    scopeChain: [
    	LocaL<anonymous>,  / / anonymous function execution context variable object, namely FunctionExecutionContext. VO
        Global<window>  / / global execution context variable object, namely GlobalExecutionContext. VO].this<numberArr reference> // This refers to numberArr this == numberArr value is true} // GlobalExecutionContext = {VO: Function numberArr: Array[5][f(), f(), f(), f(), f(), f(), f(), f()], I: 5,}, scopeChain: [Global < window > / / Global execution context variable object, namely GlobalExecutionContext. VO], this: < window reference >}Copy the code

When an anonymous function is called, the value of the variable object in the execution context of the function is empty. Therefore, when the anonymous function returns I, the value of the variable object in the execution context of the function is not found in its own variable object, so the function searches the variable object in the Global

along its scopeChain, and returns 5.

The numberArr variable that follows calls the first, second… The same goes for the fourth element, which returns 5.

Code four

var numberArr = [];
for(let i = 0; i<5; i++){
    numberArr[i] = function(){
        returni; }}console.log(numberArr[0] ());console.log(numberArr[1] ());console.log(numberArr[2] ());console.log(numberArr[3] ());console.log(numberArr[4] ());Copy the code

This code is basically the same as the above code, except that we declare the variable I that controls the number of times in the loop with the let keyword. Then we start our analysis.

step 1

First is the creation phase of the global execution context:

/ / execution stack
ExecutionStack = [
    GlobalExecutionContext    // Global execution context
]
// Global execution context
GlobalExecutionContext = {
    VO: {
    	numberArr: undefined
    },
    scopeChain: [
       Global<window>  / / global execution context variable object, namely GlobalExecutionContext. VO].this: <window reference>
}
Copy the code

Because there is no variable promotion for the let keyword, there is no variable I in the variable object of the global execution context.

step 2

When code is executed line by line, the execution phase of the global execution context begins.

Here is the code execution entering the first loop:

/ / execution stack
ExecutionStack = [
    GlobalExecutionContext    // Global execution context
]
// Global execution context
GlobalExecutionContext = {
    VO: {
        // Number is an Array of length 1, and the first element is a Function
    	numberArr: Array[1][f()], 
    },
    scopeChain: [
       Block,           // The for loop defined by let forms a block-level scope
       Global<window>  / / global execution context variable object, namely GlobalExecutionContext. VO].this: <window reference>
}
Copy the code

As you can see, when the loop starts executing, because the let keyword is encountered, a block-level scope is created that contains the value of variable I. This block-level scope is critical because it holds the value of the variable during the loop, making this code run differently from the previous code.

step 3

When I is 5:

Omit the execution context contents of I =1, I = 3, and I = 4.

GlobalExecutionContext = {
    VO: {
        // Number is an Array of type 2, and each element is Function
    	numberArr: Array[5][f(), f(), f(), f(), f()],
    },
    scopeChain: [
        Block, 
        Global<window>].this: <window reference>
}
Copy the code

The value of variable I in the block-level scope is also updated to 5.

step 4

It then calls the first element in the array and calls an anonymous function, which creates a function execution context when executed.


/ / execution stack
ExecutionStack = [
    FunctionExecutionContext, // Anonymous function execution context
    GlobalExecutionContext    // Global execution context
]
// Anonymous function execution context
FunctionExecutionContext = {
    VO: {},    // The variable object is empty
    scopeChain: [
    	LocaL<anonymous>,  / / anonymous function execution context variable object, namely FunctionExecutionContext. VO
        Block,   // block-level scope
        Global<window>  / / global execution context variable object, namely GlobalExecutionContext. VO].this<numberArr reference> // This refers to numberArr this == numberArr value is true} // GlobalExecutionContext = {VO: Function numberArr: Array[5][f(), f(), f(), f(), f(), f(), f()],}, scopeChain: [ Global<window> ], this: <window reference> }Copy the code

Because this anonymous function holds the variable I defined by the let keyword, the scope chain holds the block-level scope created during the first loop. This block-level scope, as we mentioned earlier and seen in the browser debugger, holds the value of I for the current loop.

So when RETURN I, the variable object in the current execution context is empty, we look down the scope and find the corresponding variable I in the Block, so return 0; NumberArr [1](), numberArr[2](),… And numberArr[4]() is the same.

The code for five

var scope = "global scope";
function checkscope(){
    var scope = "local scope";
    function f(){
        return scope;
    }
    return f();
}
checkscope();
Copy the code

This code includes the following is in the process of combing through this article, see a very interesting example, so posted here with you to analyze.

step 1

The code enters the global environment and begins the creation phase of the global execution context:

/ / execution stack
ExecutionStack = [
    GlobalExecutionContext    // Global execution context
]
// Global execution context
GlobalExecutionContext = {
    VO: {
        scope: undefined.checkscope<Function checkscope>, // the Function can be called}, scopeChain: [Global < window > / / Global execution context variable object, namely GlobalExecutionContext. VO], this: < window reference >}Copy the code

step 2

The execution phase of the global execution context:

/ / execution stack
ExecutionStack = [
    GlobalExecutionContext    // Global execution context
]
// Global execution context
GlobalExecutionContext = {
    VO: {
        scope: 'global scope'.// Variable assignment
        checkscope<Function checkscope>, // the Function can be called}, scopeChain: [Global < window > / / Global execution context variable object, namely GlobalExecutionContext. VO], this: < window reference >}Copy the code

step 3

When the code executes to the last line: checkScope (), the creation phase of the checkScope function execution context begins.

/ / execution stack
ExecutionStack = [
    CheckScopeExecutionContext,  // CheckScope function execution context
    GlobalExecutionContext    // Global execution context
]
// Function execution context
CheckScopeExecutionContext = {
    VO: {
        scope: undefined.f<Function f>, // the Function can already be called}, scope: [ Local<checkscope>, / / checkscope execution context variable object Namely CheckScopeExecutionContext. VO Global < window > / / Global execution context variable object Namely GlobalExecutionContext. VO], This: <window reference>} // GlobalExecutionContext = {VO: {scope: 'global scope', checkScope: < XSL: checkscope >,} / / Function can be invoked, scopeChain: [Global < window > / / Global execution context variable object, namely GlobalExecutionContext. VO], this: <window reference> }Copy the code

step 4

This is followed by the execution phase of the checkScope function execution context:

/ / execution stack
ExecutionStack = [
    CheckScopeExecutionContext,  // Function execution context
    GlobalExecutionContext    // Global execution context
]
// Function execution context
CheckScopeExecutionContext = {
    VO: {
        scope: 'local scope'.// Variable assignment
        f<Function f>, // the Function can already be called}, scope: [ Local<checkscope>, / / checkscope execution context variable object Namely CheckScopeExecutionContext. VO Global < window > / / Global execution context variable object Namely GlobalExecutionContext. VO], This: <window reference>} // GlobalExecutionContext = {VO: {scope: 'global scope', checkScope: < XSL: checkscope >,} / / Function can be invoked, scopeChain: [Global < window > / / Global execution context variable object, namely GlobalExecutionContext. VO], this: <window reference> }Copy the code

step 5

When executing to return f(), it enters the creation stage of f function execution context:

// The creation phase of the function execution context
FExecutionContext = {
    VO: {},
    scope: [
        Local<f>,    // the variable object of the execution context is fexecutionContext.vo
        Local<checkscope>,  / / checkscope execution context variable object Namely CheckScopeExecutionContext. VO
        Global<window>  / / global execution context variable object Namely GlobalExecutionContext. VO].thisWindow reference: < >} / / function execution context CheckScopeExecutionContext = {VO: {scope: 'local scope', f: <Function f>, // the Function can already be called}, scope: [ Local<checkscope>, / / checkscope execution context variable object Namely CheckScopeExecutionContext. VO Global < window > / / Global execution context variable object Namely GlobalExecutionContext. VO], This: <window reference>} // GlobalExecutionContext = {VO: {scope: 'global scope', checkScope: < XSL: checkscope >,} / / Function can be invoked, scopeChain: [Global < window > / / Global execution context variable object, namely GlobalExecutionContext. VO], this: <window reference> }Copy the code

When f function returns scope variable, there is no variable named scope in the variable object of the current f execution context, so look up the scope chain and find that the variable object of the checkScope execution context Local< checkScope > contains scope variable. So return local Scope.

Code six

var scope = "global scope";
function checkscope(){
    var scope = "local scope";
    function f(){
        return scope;
    }
    return f;
}
checkscope()();
Copy the code

This code is very similar to the above code, except that the return value of the checkScope function does not call f directly, but returns f function, calling f function in the global environment.

step 1

Creation phase of the global execution context:

/ / execution stack
ExcutionStack = [
    GlobalExcutionContext
];
// Create the global execution context
GlobalExecutionContext = {
    VO: {
        scope: undefined.checkscope<Function checkscope>, // the Function can be called}, scopeChain: [Global < window > / / Global execution context variable object, namely GlobalExecutionContext. VO], this: < window reference >}Copy the code

step 2

The execution phase of the global execution context:

/ / execution stack
ExcutionStack = [
   GlobalExcutionContext    // Global execution context
];

// Global execution context
GlobalExecutionContext = {
    VO: {
        scope: 'global scope'.// Variable assignment
        checkscope<Function checkscope>, // the Function can be called}, scopeChain: [Global < window > / / Global execution context variable object, namely GlobalExecutionContext. VO], this: < window reference >}Copy the code

step 3

When the code reaches the last line: checkScope ()(), checkScope () is executed first, which begins the creation phase of the checkScope function execution context.


/ / execution stack
ExcutionStack = [
    CheckScopeExecutionContext,     // CheckScope function execution context
    GlobalExcutionContext           // Global execution context
]
The checkScope function performs the creation phase of the context
CheckScopeExecutionContext = {
    VO: {
        scope: undefined.f<Function f>, // the Function can be called}, scopeChain: [ Local<checkscope>, / / checkscope execution context variable object Namely CheckScopeExecutionContext. VO Global < window > / / Global execution context variable object Namely GlobalExecutionContext. VO], This: <window reference>} // GlobalExecutionContext = {VO: {scope: 'global scope', checkScope: <Function checkscope>, scopeChain: [Global<window>], this: <window reference>}Copy the code

step 4

This is followed by the execution phase of the checkScope function execution context:

/ / execution stack
ExcutionStack = [
    CheckScopeExecutionContext,     // CheckScope function execution context
    GlobalExcutionContext           // Global execution context
]
// CheckScope function execution context
CheckScopeExecutionContext = {
    VO: {
        scope: 'local scope'.f<Function f>, // the Function can be called}, scopeChain: [ Local<checkscope>, / / checkscope execution context variable object Namely CheckScopeExecutionContext. VO Global < window > / / Global execution context variable object Namely GlobalExecutionContext. VO], This: <window reference>} // GlobalExecutionContext = {VO: {scope: 'global scope', checkScope: <Function checkscope>, scopeChain: [Global<window> // Global execution context variable object], this: <window reference>}Copy the code

step 5

When executing to return f, this is different from the previous code and f function is not called, so the execution context of f function will not be created. Therefore, function F will be directly returned. At this time, the checkScope function is completed, and the checkScope execution text will pop up from the execution stack.

/ / execution stack (as the CheckScopeExecutionContext ejected from the stack)
ExcutionStack = [
    GlobalExecutionContext  // Global execution context
];
// Global execution context
GlobalExecutionContext = {
    VO: {
        scope: 'global scope'.checkscope: <Function checkscope>, scopeChain: [Global<window>, this: <window reference>}Copy the code

step 6

In step3, the first half of the checkscope()() code completes and returns the f function; Then the second half () is executed, which calls the f function. Now enter the creation phase of the f function execution context:

/ / execution stack
ExcutionStack = [
    fExecutionContext,     F function execution context
    GlobalExecutionContext  // Global execution context
];

F function execution context
fExecutionContext = {
    VO: {},   // the variable object of f function is empty
    scopeChain: [
        Local<f>,          // f function execution context variable object
        Local<checkscope>, The checkScope function executes the variable object of the context
        Global<window>,    // The variable object of the global execution context].this<window reference>} // GlobalExecutionContext = {VO: {scope: 'global scope', checkScope: <Function checkscope>, scopeChain: [Global<window>], this: <window reference>}Copy the code

We can see that in the creation stage of f function execution context, its variable object is an empty dictionary, and its scope chain stores the variable object of the checkScope execution context. Therefore, when the code executes to return scope, it does not find scope variable in the variable object of F function, so it follows the scope chain. The scope variable was found in the chckScope execution context’s variable object Local< checkScope >, so return Local Scope.

conclusion

I believe that many people, like me, at the beginning of learning and understanding the execution of Yamashita, will be confused about the execution context of JavaScript because the concept is too abstract and there is no appropriate practice. The author also spent a long time reading a lot of relevant books and articles, and added some practices to sort out this article, hoping to give you some help. If there is any mistake in the description of the article, I also hope to give you valuable comments and suggestions.

At the end of the article

If this article helps you, ❤️ follow + like + favorites + comment + forward ❤️ to encourage the author

Article public number first, focus on unknown treasure program yuan for the first time to get the latest article

Ink ❤ ️ ~