What is the relationship between context and scope? The concept seems simple, but many people can’t explain the relationship. Context and scope are knowledge of compilation principle, specific programming language has specific implementation rules, this article is concerned with the implementation of JavaScript language.

Context and scope

Context is the smallest set of data that a program needs to run. Context can be understood by context switch. In a multi-process or multi-threaded environment, task switching first interrupts the current task and transfers computing resources to the next task. Because the previous task will be resumed later, the interrupt saves the scene, the context of the current task, also known as the environment.

Scope is the scope within which an identifier (variable) is visible in a program. Scoped rules are rules that maintain visibility of identifiers to determine their access by currently executing code. Scopes are determined under specific scope rules.

Context, environment is sometimes called scope, that is, the two concepts are sometimes used interchangeably; Context, however, refers to the overall environment, and scope is concerned with the accessibility (visibility) of identifiers (variables). The context is determined, and the scope is determined according to the scoping rules of the specific programming language. This is how context relates to scope.

function callWithContext(fn, context) {
  return fn.call(context);
}

let name = 'Banana';

const apple = {
  name: "Apple"
};
const orange = {
  name: "Orange"
};

function echo() {
  console.log(this.name);
}

echo(); // Banana
callWithContext(echo, apple);  // Apple
callWithContext(echo, orange); // Orange
Copy the code
var a = 1;
function foo(){
    Return an arrow function
    return (a)= > {
        // This inherits from foo()
        console.log( this.a );
    };
}
var obj1 = {
    a:2
};
var obj2 = {
    a:3
};

foo()() / / 1
var bar = foo.call( obj1 ); // Call location
bar.call( obj2 ); / / 2
foo.call( obj2 )(); / / 3
Copy the code


Second, JavaScript execution

The whole execution process of JavaScript code is divided into two stages: code compilation stage and code execution stage. The compilation phase is done by the compiler, translating code into executable code, where scoping rules are determined. The execution phase is done by the engine and the main task is to execute executable code. The execution context is created in this phase.

When JavaScript code executes into an environment, an execution context is created for that environment, which does some preparatory work before you run the code, such as scoping, creating local variable objects, and so on.

The execution environment of the JS code

  1. The global environment
  2. Function of the environment
  3. Eval function environment (not recommended)

The type of execution context

  1. Global execution context
  2. Function execution context
  3. The eval function executes the context


Execution context

The JavaScript runtime first enters the global environment, which generates the global context. There are basically functions in the program code, so calling the function will enter the function execution environment, and the corresponding execution context of the function will be generated.

In functional programming, more than one function is declared in the code and the corresponding execution context exists. In JavaScript, execution context is managed by Stack access, which we can Call the execution Stack, or Call Stack. The bottom of the stack is always the global context, and the top is the context currently executing.

When a program executes into an execution environment, its execution context is created and pushed onto the execution stack (pushed). When the program completes execution, its execution context is destroyed and pushed off the stack, handing control to the next execution context. The stack structure

The global context is always at the bottom of the stack because the global context is entered first in JS execution. The “top of the stack is the execution context of the currently executing function”, which is pushed off the top of the stack when the function call completes.

“There is only one global environment, and there is only one global execution context, and it is only pushed off the execution stack when the page is closed, otherwise it remains at the bottom of the stack.”

let color = 'blue';

function changeColor() {
  let anotherColor = 'red';

  function swapColors() {
      let tempColor = anotherColor;
      anotherColor = color;
      color = tempColor;
  }

  swapColors();
}

changeColor();
Copy the code
  • There is only one global context, which is pushed out when the browser closes
  • There is no limit to the number of execution contexts a function can have
  • Every time a function is called, a new execution context is created for it, even for the called function itself


Lexical scope

The scope chain is composed of a series of variable objects of the current environment and the upper environment, which ensures the orderly access of the current execution environment to the variables and functions that meet the access permissions.

In JavaScript, this concrete scoping rule is lexical scope, which is the rule of the scope chain in JavaScript. Lexical scope is a variable that is determined at compile time (lexical stage), so lexical scope is also called static scope, as opposed to dynamic scope.

let a = 2;

function foo() {
  console.log(a);
  // Will output 2 or 3?
}

function bar() {
  let a = 3;
  foo();
}

bar();
Copy the code

As mentioned earlier, lexical scope is also called static scope, and variables are determined at lexical stage, that is, at definition time. Although called inside bar, because Foo is a closure function, it retains its scope even if it executes outside of its own lexical scope. The so-called closure function, that is, the function closed its own definition of the environment, forming a closure. Closures are a combination of functions and the lexical environment in which they were created. This environment contains all the local variables that can be accessed when the closure is created.) So Foo doesn’t look for variables in the bar, which is the nature of static scope.

Dynamic scopes don’t care how or where functions and scopes are declared, only where they are called from. In other words, the scope chain is based on the call stack, not the scope nesting in the code.

Lexical scope is determined at code writing or definition time, whereas dynamic scope is determined at run time. Lexical scopes focus on where functions are declared, while dynamic scopes focus on where functions are called from.

function foo() {
  let a = 0;
  function bar() {
    console.log(a);
  }
  return bar;
}

let a = 1;
let sub = foo();

sub(); / / 0;
Copy the code

Once the default values of parameters are set, the parameters form a separate context when the function is declared initialized. After initialization, the scope will disappear. This syntactic behavior does not occur if the parameter defaults are not set.

var x = 1;
function foo(x, y = function() { x = 2; {})var x = 3;
  y();
  console.log(x);
}

foo() / / 3
x / / 1
Copy the code

In the code above, the arguments to function foo form a single scope. In this scope, we first declare the variable x, then we declare the variable y, and the default value of y is an anonymous function. The variable x inside this anonymous function points to the first parameter x of the same scope. The internal variable x is declared inside the function foo. This variable is not the same as the first parameter x because it is not in the same scope. Therefore, the internal variable x and the external global variable x are not changed after y.


The application of closures

Modularization, Currization, simulation of block-level scope, namespace, cache data

const tar = (function () {
    let num = 0;
    return {
        addNum: function () {
            num++;
        },
        showNum: function () {
            console.log(num);
        }
    }
})()
tar.addNum();
tar.showNum();
Copy the code
let add = function(x){
  return function(y){
    return x + y
  }
}
console.log(add(2)(4)) // 6
Copy the code
for (var i = 1; i < 5; i++) {
  setTimeout(function timer() {
    console.log(i);
  }, 0);
}

function func(){
  for(var i = 0; i < 5; i++){  
    + (i => { setTimeout(() => console.log(i),300) })(i)
  }
}
func()
Copy the code
var MyNamespace = {}; Mynamespace.dosomething = function (){var label, icon; Function setLabel(){// do something... } this.getLabel = function(){// do something... }; } / / this method can be external access, but only by taking MyNamespace/assignment, access to the private class variables. The TreeItem. Prototype = {print: the function () {the console. The log (enclosing getLabel ()); }}Copy the code
import {readFileSync, readdirSync} from 'fs'; var readContent = (function(){ let contentCache = {}; return (bookName)=>{ let content = contentCache[bookName]; if (! content){ content = readFileSync(bookName+".txt", "utf8"); contentCache[bookName] = content; } return content; }; }) ();Copy the code


Reference article:

  • Detailed diagrams of execution context
  • From context, to scope
  • Execution context and execution stack, variable object
  • Lexical scope VS dynamic scope