Execution context

An execution context is an abstraction of the context in which the current JavaScript code is being parsed and executed.

  • Global execution context: default, lowest level, globally unique;
  • Function execution context: Each time a function is called, a new execution context is created for the function, including the call itself;
  • Eval function execution context: Code running in the Eval function has its own execution context.

Execution context stack

In a JavaScript program, there are bound to be multiple execution contexts, and the JavaScript engine handles them in a stack, which we call the Call stack. The bottom of the stack is always the global context, and the top is the context currently executing.

When the browser first loads the script, the global execution context is entered by default. Each time a function call occurs, a new execution context is created and pushed to the top of the execution stack. The browser will always execute the execution context at the top of the stack, and once the current context function completes execution, it will be ejected from the top of the stack, giving control of the context to the current stack.

Creation and execution of the execution context

Before any JavaScript code is executed, the execution context is created. In total, three things happen during the creation phase:

  1. Determine the value of this, also known as this Binding.
  2. The LexicalEnvironment component is created.
  3. The VariableEnvironment component is created.

Thus, the execution context can be conceptually expressed as follows:

ExecutionContext = {  
  ThisBinding = <this value>, LexicalEnvironment = { ... }, VariableEnvironment = { ... }},Copy the code

This Binding

  • In the context of global execution, the value of this points to the global object, and in the browser, the value of this points to the window object
  • In the context of function execution, the value of this depends on how the function is called. If it is called by an object reference, the value of this is set to that object, otherwise the value of this is set to global or undefined (in strict mode)
  • In the constructor pattern, the occurrence of this. XXX = XXX in the class (in the function body) is an instance of the current class
  • Call, apply, and bind: this is the first argument
  • If the arrow function does not have its own this, see if the outer function has a function. If so, this of the outer function is this of the inner arrow function. If not, this is window

Lexical environment

The lexical environment consists of two parts:

  • EnvironmentRecord: The actual location where variable and function declarations are stored
  • References to external environments (outer) : Access the external lexical environment

Lexical environments fall into two types:

  • Global lexical environment:
    • EnvironmentRecord callObject Environment RecordContains a global object (window object) with its associated methods and properties (such as array methods) and any user-defined global variables to which the value of this refers.
    • For the outernull.
  • Functional lexical environment:
    • EnvironmentRecord callDeclarative environmental recordsIn addition to the corresponding variable and function declarations, contains oneargumentsObject containing the mapping between the index and the parameters passed to the function and the length (number) of the parameters passed to the function;
    • Outer can be either a global environment or an external function environment that contains internal functions.

Abstractly, the lexical environment in pseudocode looks like this:

GlobalExectionContext = {  
  LexicalEnvironment: {  
    EnvironmentRecord: {  
      Type: "Object".// The identifier is bound here
    outer: <null>  
  }  
}

FunctionExectionContext = {  
  LexicalEnvironment: {  
    EnvironmentRecord: {  
      Type: "Declarative".// The identifier is bound here
    outer: <Global or outer function environment reference>}}Copy the code

The variable environment

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

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

For example, the code:

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

Corresponding execution context:

GlobalExectionContext = {

  ThisBinding: <Global Object>, LexicalEnvironment: {EnvironmentRecord: {Type: "Object", // < initialized >, multiply: // Global variable declaration (let or const variable, not initialized when created, use error before declaration, execution phase will be set to undefined) < func > // global function declaration} outer: <null>}, VariableEnvironment: {EnvironmentRecord: {Type: "Object", } outer: <null>}} FunctionExectionContext = {ThisBinding: <Global Object>, LexicalEnvironment: {EnvironmentRecord: {Type: "Declarative", // Outer: <GlobalLexicalEnvironment>}, VariableEnvironment: {EnvironmentRecord: {Type: D: <GlobalLexicalEnvironment>}}Copy the code

Declarations of variables and functions are improved

Knowing the two phases of the execution context, it is easy to declare the concept of promotion.

Here’s an example:

(function() {

    console.log(typeof foo); // Function pointer
    console.log(typeof bar); // undefined

    var bar = function() {
            return 'world';
        };

    function foo() {
        return 'hello';
    }
    
    var foo = 'hello';
    console.log(foo); // hello} ());Copy the code
  1. Why can we access foo before it is declared?

During the creation phase, variables and functions are defined and therefore accessible, but without concrete values.

  1. Foo is declared twice. Why is foo shown as a function instead of undefined or a string? And then foo is a string again, right?

In the creation phase, the function declaration is created first and refers to the function’s in-memory reference. When the function declaration with the same name is encountered, the pointer is overwritten. When the variable declaration with the same name is encountered, the pointer is skipped. That is, function declarations have a higher priority. But function variables can be reassigned, so foo = ‘hello’ is executed and foo changes.

  1. Why is bar undefined?

Bar is actually still a variable, but its value is a function, and the variable is created and initialized to undefined during the creation phase.

Note: only var and function declarations are promoted; let, const, class, and so on are not.

scope

Scope is the accessible scope of variables and functions, that is, scope controls the visibility and life cycle of variables and functions.

Classification of scopes

Similarly, scopes can be global or local.

The global scope includes the following scenarios:

  1. Outermost functions and variables defined outside the outermost function have global scope
  2. All variables that do not define a direct assignment are automatically declared to have a global scope
  3. All properties of window objects have global scope

Note: try not to define variables in the global scope, otherwise it will pollute the global namespace, easy to cause naming conflicts, resulting in variable pollution.

A local scope, also called a function scope, is a variable declared inside a function. It can only be accessed locally.

Here’s an example:

var a=1;
function fn(){
    var sum=0;
    alert(a); // 1 Local access global} fn ();/ / perform firstAlert (sum);// A global access local error was reported
Copy the code

The difference between scope and execution context

  • The scope is determined when the function is defined, not when the function is called;
  • There are no variables in a scope. The value of the variable must be obtained by the execution context of the scope.
  • Under the same scope, different calls will produce different execution contexts, which will produce different values of variables;
  • The value of a scope variable is determined during execution;

The scope chain

Everything in JavaScript is an object, functions are objects. Function objects, like any other object, have properties that can be accessed through code and a set of internal properties that are only accessible to the JavaScript engine. One of the internal properties is [[Scope]], which contains the set of objects in the Scope that the function is created with. This set is called the function’s Scope chain, and it determines which data can be accessed by the function.

That is, the set of scopes is the scope chain.

In the process of executing a function, it first looks for a variable from its own internal scope. If it cannot find one, it looks for it from the scope in which the current function was created, and then upward, that is, up one level, until it finds the global scope.

Note: The deeper the identifier, the slower the read and write speed, and from this perspective, the use of global variables should be minimized.

Finally, an example explains the scope of free variables and create functions:

var x = 10;
function fn() {
    console.log(x);
}

function show1(f) {
    var x = 20;
    f(); // 10 instead of 20, the value of x is to be found in the scope where fn is created, wherever it is called
}
function show2() {
    var y = 20;
    console.log(x + y); // x is a free variable, that is, a variable not declared in the current scope is called a free variable
}
show1(fn);
Copy the code

closure

What is a closure

The concept of closures: functions that can read variables inside other functions.

Illustrate two cases of closures: functions as return values and functions as arguments.

var x = 10;
function fn() {
    var x = 20;
    return function f() {
        console.log(x);
    };
}

var show = fn();
// fn() returns a function. Functions are special in that they can create a separate scope.
// The function body is returned with a free variable x that refers to x in the context of fn's scope.
// So the execution context of fn() is not destroyed at this point and the global context becomes active

show(); // the function returns the value 20
Copy the code
var x = 10;
function fn() {
    console.log(x);
}

(function (f) {
    var x = 20;
    f(); 
})(fn); // the function is passed as an argument, and x is 10, in the scope where fn is created
Copy the code

【 Summary 】 The conditions for the formation of closure are as follows: there is function nesting, and internal function has reference to external function; What closures mean and do: Enable functions to be executed outside of their current scope.

Note: Using closures can increase memory overhead or cause memory leaks

Application scenarios for closures

  1. Encapsulate private variables and methods to avoid global contamination
var counter = (function(){
    var privateCounter = 0; // Private variables
    function change(val){
        privateCounter += val;
    }
    return {
        increment:function(){   // Three closures share a lexical environment
            change(1);
        },
        decrement:function(){
            change(-1);
        },
        value:function(){
            return privateCounter;
        }
    };
})();
// The shared environment is created in the body of an anonymous function and executed immediately.
// There is a local variable and a local function in the environment, accessed through the three public functions of the object returned by the anonymous function.

console.log(counter.value());/ / 0
counter.increment();
counter.increment();/ / 2
Copy the code
  1. Simulate block-level scope (anonymous self-executing functions)
function fn(num){
    for(var i=0; i<num; i++){}console.log(i);// I does not fail outside of for
}
fn(2);

if(true) {var a=13;
}
console.log(a);// Variables defined in if can be accessed externally

/ / modified
(function(){  // I is not recognized externally
    for(var i=0; i<count; i++){} })();console.log(i);  // An error was reported
Copy the code

Note: When a variable is defined through a loop using let, it is declared for each iteration and let has its own block-level scope.

  1. Timers, event listeners, Ajax requests, and many other things that use callbacks are actually using closures
/ / timer
function sayHello(name){
    setTimeout(function(){
	console.log('hello '+name);
    },1000); } sayHello (' hi');
Copy the code
// Event listener
function changeSize(size){
    return function(){
        document.body.style.fontSize = size + 'px';
    };
}

var size12 = changeSize(12);
var size16 = changeSize(16);

document.getElementById('size-12').onclick = size12;
document.getElementById('size-16').onclick = size16;
Copy the code

reference

  1. Understand the execution context of JavaScript
  2. Understand Javascript execution context and execution stack
  3. In-depth understanding of JavaScript scopes
  4. In-depth understanding of javascript Scope Chain
  5. In-depth understanding of javascript prototypes and closures (completion)
  6. About closures and application scenarios in JavaScript
  7. Interviewer: Talk about your understanding of JS closures and common application scenarios (functions of closures)