JavaScript scope, execution context and closure


The content is a summary of the blogs written by Changmiao and Frozen, and a little bit of my own experience. Hahahaha!

Scope


Definition :

1. Scope refers to the area where variables are defined in the program source code

2. The scope specifies how to find variables, that is, to determine the access rights of the currently executing code to the variables

Lexical scope and dynamic scope


JavaScript uses lexical scope (also called static scope), and the scope of a function is determined when the function is defined.

The opposite of lexical scope is dynamic scope. The scope of a function is determined when the function is called.

one example:

var a = 1;

function foo() {
    
    
    console.log(a);
}

function bar() {
    
    
    var a = 2;
    foo();
}

bar(); //其结果是1

1. If JavaScript uses lexical scope, the execution process is:

Execute the foo function, first look for the local variable a inside the foo function, if not, look for the code at the level above it (var a = 1) according to the writing position, so it will print 1

2. If JavaScript uses dynamic scope, its execution process is:

Execute the foo function, first look for the local variable a inside the foo function, if not, look for the a variable from the bar function scope that calls the foo function, and look for the code above it (var a = 1), so Will print 2

Conclusion: The lexical scope of JavaScript

Execution context


Definition : Execution context is an abstract concept of the environment in which JavaScript code is evaluated and executed. Whenever JavaScript code is running, it runs in the execution context

JavaScript execution order

When JavaScript code is executed, the JavaScript engine does not analyze and execute the program line by line, but analyzes and executes it piece by piece. When a piece of code is executed, a "preparation work" is performed. So how is each piece of code that will be "prepared" divided?

Executable code

There are three types of JavaScript executable codes, namely:

1. Global code
2. Function code
3. Eval code (not commonly used)

For example, when a function is executed, "preparation work" will be performed. The "preparation work" here is more professionally called "execution context" here, which is called the function execution context.

Give an example to illustrate the global code and executable code~:

Global code

let str = 'hello world';
function foo() {
    
    
  // 函数体中的代码不算全局代码
}
console.log(str); // hello world

Function code

function foo() {
    
    
  console.log('I like JavaScript'); // 函数代码
  console.log('hello world'); // 函数代码
}

Execution context stack

There are many functions in the code, so how should we manage so many execution contexts?

The JavaScript engine creates an Execution context stack (ECS) to manage the created execution context

The following is an example to simulate the behavior of the execution context stack~:

1. In order to simulate the behavior of the execution context stack, the execution context stack is an array:

ECStack = [];

2. When JavaScript starts to execute the interpreted code, the first encounter is the global code, so when it is initialized, a global execution context is pushed into the execution context stack. We use globalContext to represent it, and before the end of the program, this The global execution context will always exist.

ECStack = [
    globalContext
];

Now we will simulate the behavior of the execution context with the following code block~:

function fun3() {
    
    
    console.log('好困')
}

function fun2() {
    
    
    fun3();
}

function fun1() {
    
    
    fun2();
}

fun1();

When a function is executed, a context is created and pushed onto the stack, and when the function is executed, the execution context of the function will be removed from the stack.

// fun1()
ECStack.push(<fun1> functionContext);

// fun1中调用了fun2 还需要创造fun2的执行上下文
ECStack.push(<fun2> functionContext);

// fun2中调用了fun3 还需要创造fun3的执行上下文
ECStack.push(<fun3> functionContext);

// fun3执行完毕
ECStack.pop();

// fun2执行完毕
ECStack.pop();

// fun1执行完毕
ECStack.pop();

Create execution context

For each execution context, there are three important attributes:

1. Variable object (Variable object, VO)
2. Scope chain
3.this

Variable object

Definition : The variable object is the data scope related to the execution context, which stores the variable and function declarations defined in the context

Global context

The variable object of the global context is the global object window (browser)

Function context

In the context of the function, we use the activation object (AO) to represent the variable object (VO)

Activation object (AO): Before entering the execution phase, none of the attributes in the variable object (VO) can be accessed, but after entering the execution phase, the variable object (VO) is transformed into an active object (AO), and the attributes inside are all Can be accessed, and then start the execution phase of the operation. VO and AO are different names of the same object in different life cycles

Execution context

The execution context code will be processed in two stages:

1. Enter the execution context
2. Code execution

Give an example to illustrate the changes of AO in different stages:

function foo(a) {
    
    
  var b = 2;
  function c() {
    
    }
  var d = function() {
    
    };
}

foo(1);
1. Enter the execution context

Variable objects will include:

1. All formal parameters of the
function 2. Function declaration
3. Variable declaration

AO at this time:

AO = {
    
    
    arguments: {
    
     // aruguments是一个对应传给函数的参数的类数组对象
        0: 1,    //arguments[0] = 1
        length: 1  // arguments.length = 1
    },
    a: 1,
    b: undefined,
    c: reference to function c(){
    
    },
    d: undefined
}
2. Code execution

AO at this time:

AO = {
    
    
    arguments: {
    
    
        0: 1,
        length: 1
    },
    a: 1,
    b: 3,
    c: reference to function c() {
    
    },
    d: reference to FunctionExpression "d"
}
Exercise
function foo() {
    
    
    console.log(a);
    a = 2;
}

foo();  // a is not defined

function bar() {
    
    
    a = 2;
    console.log(a);
}

bar();  // 2


//两端代码执行console的时候 AO:
AO = {
    
    
    arguments: {
    
    
        length: 0
    }
}
函数中的 "a" 并没有通过 var 关键字声明,所有不会被存放在 AO 中。
但是由于第二段代码 a = 2; 位于 consol.log(a)之前,所以在执行console的时候全局变量a = 2

Scope chain

Definition : When looking up a variable, it will first look for the variable object of the current context. If it is not found, it will look for the variable object of the parent execution context, and always find the variable object of the global context, which is the global object. In this way, a linked list composed of multiple execution context variable objects is called a scope chain.

[[scope]] internal properties, when the function is created, all parent variables will be saved in it, which can be understood as a function [[scope]] is the hierarchical chain of all parent variable objects

For example~:

function foo() {
    
    
    function bar() {
    
    
        ...
    }
}

The respective [[scope]] above are:

foo.[[scope]] = [
    globalContexts.VO
];

bar.[[scope]] = [
    fooContexts.AO,
    globalContexts.VO
];

Let us take an example to take a closer look at the creation process of the scope chain and variable objects in the context of function execution:

var scope = "global scope";
function checkscope() {
    
    
    var scope2 = "local scope";
    return scope2;
    }
checkscop();

The execution process is as follows:

1. Create a checkscope function and save its scope chain to internal properties [[scope]]

checkscope.[[scope]] = [
    globalContext.VO // 全局代码的变量对象
];

2. Execute the checkscope function, create the execution context of the checkscope function, and push the execution context of the checkscope function onto the stack

ECStack = [
    checkscopeContext, //checkscope函数执行上下文
    globalContext //全局执行上下文
];

3. The checkscope function is not executed immediately, start to make preparations, the first step: copy the function scope attribute to create a scope chain

checkscopeContext = {
    
    
    Scope: checkscope.[[scope]], //将第一步函数的scope属性复制,创造作用域链
}

4. Step 2: Use arguments to create an active object, then initialize the active object, add formal parameters, function declarations, and variable declarations (that is, enter the execution context during context execution)

checkscopeContext = {
    
    
    AO: {
    
    
        arguments: {
    
    
            length: 0
        },
        scope2: undefined
    }
    Scope: checkscope.[[scope]],
}

5. Push the active object into the top of the checkscope scope (that is, function activation)

checkscopeContext = {
    
    
    AO: {
    
    
        arguments: {
    
    
            length: 0
        },
        scope2: undefined
    },
    Scope: [AO, globalConext.[[Scope]]]
}

6. Start executing the function and modify the attribute value of AO

checkscopeContext = {
    
    
    AO: {
    
    
        arguments: {
    
    
            length: 0
        },
        scope2: 'local scope'
    },
    Scope: [AO, globalContext.[[Scope]]]
}

7. The function is executed and popped from the execution context stack

ECStack = [
    globalContext
];

Closure

Definition: A closure is a function that has access to variables in the scope of another function

Example ~

var data = [];

for (var i = 0; i < 3; i++) {
    
    
  data[i] = function () {
    
    
    console.log(i);
  };
}

data[0]();
data[1]();
data[2]();

Before we talk about closures, let us analyze this piece of code

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

var foo = checkscope();
foo();

The execution process is as follows:

1. Encounter global code, create a global context, and push the global context onto the context stack

ECStack = [
    globalContext
];

2. Global context initialization

globalContext = {
    
    
        VO: [global],
        Scope: [globalContext.VO],
    }

3. At the same time of initialization, create a checkscope function and save its scope chain to the internal properties of the function [[scope]]

checkscope.[[scope]] = {
    
    
   globalContext.VO
}

4. Execute the checkscope function, create a checkscope function execution context, and the checkscope execution context is pushed into the execution context stack

ECSstack = [
     checkscopeContext,
     globalContext
]

5.checkscope function execution context initialization

  1. Copy function [[scope]] attribute to create scope chain
checkscopeContext = {
    
    
     scope:checkscope.[[scope]]
}
  1. Use arguments to create an active object, initialize the active object, add formal parameters, function declarations, and variable declarations
checkscopeContext = {
    
    
        AO: {
    
    
            arguments: {
    
    
                length: 0
            },
            scope: undefined,
            f: reference to function f(){
    
    }
        },
    }
  1. Push the active object to the top of the checkscope scope
checkscopeContext = {
    
    
        AO: {
    
    
            arguments: {
    
    
                length: 0
            },
            scope: undefined,
            f: reference to function f(){
    
    }
        },
        Scope: [AO, globalContext.VO],
        this: undefined
    }

6. The f function is created, and the scope is saved to the internal properties of the f function [[scope]]

fscope.[[scope]] = {
    
    
     checkscopeContext.AO, globalContext.VO
}

7.f function initialization

  1. Copy function [[scope]] attribute creation scope
  2. Use arguments to create an active object, initialize the active object at the same time, add formal parameters, variable declarations, and function declarations
  3. Move the active object to the top of the f scope chain
fContext = {
    
    
        AO: {
    
    
            arguments: {
    
    
                length: 0
            }
        },
        Scope: [AO, checkscopeContext.AO, globalContext.VO],
    }

8.f function execution, looking for scope along the scope chain

9.f function is executed, popped from the execution context stack

ECSstack = [
     checkscopeContext,
     globalContext
]

10. The checkscope function is executed and popped from the execution context stack

ECSstack = [
     globalContext
]

Guess you like

Origin blog.csdn.net/Horizonar/article/details/109912192