Код Javascript запускается с «Контекстом выполнения». Поскольку само имя включает в себя «контекст», оно представляет собой среду/обстоятельства выполнения кода. Контекст выполнения имеет все, что нужно для запуска текущего кода.

Всякий раз, когда вы запускаете код JS, должен быть контекст выполнения. Сначала JS Engine запускает JS-код, создается «Глобальный контекст выполнения», который также помещается в «Стек выполнения». и Затем всякий раз, когда вызываются какие-либо функции, создается «Контекст выполнения функции», который помещается в «Контекст выполнения».

Если вы думаете, что весь код JS представляет собой функцию, контекст выполнения этой функции — «глобальный контекст выполнения».

Компонент и состав контекста выполнения немного отличаются в разных версиях ES. Но есть общие 4 компонента.

  1. Переменная среда
  2. Лексическое окружение
  3. Справочник по внешней среде
  4. Эта привязка

Ниже в этой статье будет использоваться объект ExecutionContext. Я еще раз повторяю, что форма/состав компонента EC отличается в разных версиях es, но это не так важно для понимания контекста выполнения.

ExecutionContext = {
  VariableEnvironment = {},
  LexicalEnvironment = {}
  OuterEnvrionmentReference = null or other EC,
  thisBinding = anyother object
}

1. Переменная среда

Вы можете знать подъем, а также то, что поднимается. Это var переменная и function declaration. Как и подъемные устройства, переменная среда хранит var переменных и function declaration.

var name = "heal";
var sex = "male";
function sayHi() {
  var a = 1;
  console.log("hi")
}
sayHi();

VariableEnvironment вышеприведенного кода выглядит так, как показано ниже.

// Execution Stack
--------------------------------------------------------------------
|
| sayHi FunctionExecutionContext: {
|  VariableEnvironment: {
|    a: 1;
|  }
|}
|
--------------------------------------------------------------------|
|GlobalExecutionContext: {
|  VariableEnvironment: {
|    name: "heal";
|    sex: "male";
|    sayHi: <ref. <sayHi func>
|  },
|}
|
--------------------------------------------------------------------

Если вам интересно, почему значение функции sayHi является эталонным, посмотрите эту ссылку (управление памятью JS)

2. Лексическое окружение

Лексическая среда хранит let , const переменных.

let name = "kim";
const name2 = "Lee"

LexicalEnvironment будет выглядеть следующим образом:

ExecutionContext: {
  LexicalEnvironment: {
    name: "kim";
    name2:"Lee";
  }
}

3. Справочник по внешней среде

Другое название — Scope. Вы слышали много размаха. Вот и все.

Внешняя ссылка (внутренней) лексической среды — это ссылка на лексическую среду, которая логически окружает внутреннюю лексическую среду. (из https://262.ecma-international.org/6.0/)

Что вы должны иметь в виду, так это то, что «Думайте, что это просто место, где эта функция объявлена». Не думайте, где он вызывается. давайте посмотрим ниже коды

Внешний пример 1.

function outFn() {
  var a = 1;
  
  function inFn() {
    return a;
  }
}
outFn(); // return 1

Контекст выполнения вышеприведенного кода помещается в стек, как показано ниже.

// Execution Stack
--------------------------------------------------------------------
|
|inFn FunctionExecutionContext: {
|  variavleEnvironment: {},
|  outerEnv: outFn execution context
|}
|
--------------------------------------------------------------------
|
|outFn FunctionExecutionContext: {
|  variavleEnvironment: {
|    a: 1
|    isFn: <ref. isFn func>  
|  },
|  outerEnv: global execution context
|}
|
--------------------------------------------------------------------|
|GlobalExecutionContext: {
|  VariableEnvironment: {
|    outFn: <ref, outFn func>
|  },
|  outerEnv: null,  // global execution context's outerEnv is "null"
|}
|
--------------------------------------------------------------------

Функция isFn не имеет aпеременной. поэтому проверьте externalEnvironment (функция outFn). и может вернуть значение a.

Внешний пример 2.

Угадайте, что вернет функцияf2.

var a = 1; 
function f1() {   
  return a; 
}  
function f2() {   
  var a = 2;   
  f1(); 
} 
f2(); // ???

Функция f2 возвращает 1. Как вы догадались, он вернет 2?

В начале outerEnvя напомнил, что "представьте, что это просто место, где объявлена ​​эта функция".

Стек контекста выполнения примера 2 выглядит следующим образом:

// Execution Stack
--------------------------------------------------------------------
|
| f1 FunctionExecutionContext: {
|  variavleEnvironment: {},
|  outerEnv: global execution context // !!  CHECK THIS OUT.  !!
|}
|
--------------------------------------------------------------------
|
| f2 FunctionExecutionContext: {
|  variavleEnvironment: {
|    a: 2
|  },
|  outerEnv: global execution context
|}
|
--------------------------------------------------------------------|
|GlobalExecutionContext: {
|  VariableEnvironment: {
|    a: 1,
|    f1: <ref, f1 func>,
|    f2: <ref, f2 func>
|  },
|  outerEnv: null,  // global execution context's outerEnv is "null"
|}
|
--------------------------------------------------------------------

Внешняя среда f1 – это GlobalExecutionContext. Потому что externalEnv определяется там, где объявлена ​​эта функция.

4. эта привязка

Вы думаете, что ключевое слово this означает «кто вызывает эту функцию».

const functions = {
  sayHi: function() {
    console.log('Hi');
  };,
  ~~
}
functions.sayHi();
const sayHiGlobal = functions.sayHi;
sayHiGlobal();

Стек ExecutionContext выглядит следующим образом:

// Execution Stack
--------------------------------------------------------------------
|
| functions.sayHi ExecutionContext: {
|  thisBinding: functions object
|}
|
--------------------------------------------------------------------|
|GlobalExecutionContext: {
|  LexicalEnvironment: {
|    functions: <ref. functions.object>,
|    sayHi: <ref. functions.sayHi>
|  },
|}
|
--------------------------------------------------------------------

После возврата из functions.sayHi() контекст functions.sayHi извлекается, а контекст sayHiGlobal помещается в стек.

// Execution Stack
--------------------------------------------------------------------
|
| sayHiGlobal ExecutionContext: {
|  outerEnv: GlobalExecutionContext,
|  thisBinding: global object // 
|}
|
--------------------------------------------------------------------|
|GlobalExecutionContext: {
|  LexicalEnvironment: {
|    functions: <ref. functions.object>,
|    sayHi: <ref. functions.sayHi>
|  },
|}
|
--------------------------------------------------------------------

Напомните еще раз, что this – это "кто вызывает эту функцию".

Закрытие с контекстом выполнения

Вы можете немного больше понять, как Closure работает в контексте выполнения.

Вы уже слышали closure объяснение, похожее на приведенное ниже.

Замыкание — это комбинация объединенной (вложенной) функции со ссылками на ее окружающее состояние (лексическое окружение). Другими словами, замыкание дает вам доступ к области действия внешней функции из внутренней функции. В JavaScript замыкания создаются каждый раз, когда создается функция, во время создания функции. — закрытие МДН

Но этого недостаточно для понимания. Давайте углубимся в контекст выполнения.

function createClosure() {
  var a = 1;
  
  function inner() {
    return a;
  }
  return inner;
}
var f1 = createClosure();   // !!  POINT_1  !!
f1(); // return 1;

f1 закрывается. Потому что может получить доступ к var a. Давайте посмотрим, как функция f1() может получить доступ к переменной a.

В POINT_1 стек выполнения выглядит ниже.

// Execution Stack
--------------------------------------------------------------------
|
| createClosure FunctionExecutionContext: {
|  variableEnvironment: {
|    a:1,
|    inner: <ref. inner func>  
|  },
|  outerEnv: global execution context
|}
|
--------------------------------------------------------------------|
|GlobalExecutionContext: {
|  VariableEnvironment: {
|    f1: <ref. fn func>,
|    createClosure: <ref, createClosure func>
|  },
|  outerEnv: null,
|}
|
--------------------------------------------------------------------

После POINT_1 createClosure извлекается из стека, а замыкание создается в верхней части стека выполнения. поэтому функцияfn по-прежнему имеет доступ к переменной a.

// Execution Stack
--------------------------------------------------------------------
|
| CLOSURE: {
|  variableEnvironment: {
|    a:1,
|  },
|}
|
--------------------------------------------------------------------|
|GlobalExecutionContext: {
|  VariableEnvironment: {
|    f1: <ref. fn func>,
|    createClosure: <ref, createClosure func>
|  },
|  outerEnv: null,
|}
|
--------------------------------------------------------------------

С этим изображением: закрытие создается и помещается в стек. Лучше понять closure.

далее…

Есть еще одна важная концепция: как создается контекст выполнения.

Следующая статья будет «Контекст выполнения, созданный в два этапа: фаг создания, фаг выполнения».