Все в JS происходит внутри контекста выполнения. Представьте себе закрытый контейнер, внутри которого работает JS. Это абстрактное понятие, которое содержит информацию об окружении. выполняется в текущем коде.

В контейнере первый компонент — это компонент памяти (этап создания памяти), а второй — компонент кода (этап выполнения кода). Компонент памяти имеет все переменные и функции в парах ключ-значение. Его также называют средой переменных.
Компонент кода — это место, где код выполняется построчно. Его также называют Нитью Исполнения.

  1. Создание памяти

На этом этапе контекст выполнения сначала связывается с объектом контекста выполнения. Этот объект хранит много важных данных, которые будут использоваться на этапе выполнения кода

Эта фаза состоит из трех этапов:
1. Создание переменный объект (VO)

Это объектный контейнер, созданный в контексте выполнения. В нем хранятся переменные и объявления функций, определенные в контексте выполнения.
В глобальном контексте выполнения для переменных, объявленных с помощью ключевого слова var, в VO добавляется свойство, которое указывает на эту переменную и устанавливается в значение «undefined».
А для функций в ВО добавляется свойство, указывающее на функцию, и это свойство хранится в памяти. Это означает, что все объявления функций будут сохранены и доступны внутри ВО еще до того, как код начнет выполняться.
Контекст выполнения функции, с другой стороны, не создает ВО. Вместо этого он создает подобный массиву объект, называемый объектом «аргумент», который включает в себя все аргументы, предоставленные функции.


Этот процесс хранения переменных и объявления функции в память до выполнения кода называется подъемом.

2. Создание цепочки областей действия

Контекст выполнения каждой функции создает свою область видимости, пространство, в котором к определенным ею переменным и функциям можно получить доступ с помощью процесса, называемого определением области действия.
Когда функция определена в другой функции, внутренняя функция имеет доступ к коду. определено во внешней функции, наоборот невозможно. Такое поведение называется лексической областью действия.
Эта концепция области действия приводит к связанному явлению в JavaScript, называемому замыкания. Это когда внутренняя функция имеет доступ к внешней функции, даже после того, как выполнение внешней функции завершено. область глобального контекста выполнения для разрешения переменных и функций, вызываемых в них, называется цепочка областей действия.
Только когда механизм JavaScript не может разрешить переменную в цепочке областей видимости, она прекращает выполнение и выдает ошибку.
Однако это не работает в обратном направлении. То есть глобальная/внешняя область видимости никогда не будет иметь доступа к переменным внутренней функции, если только они не будут возвращены из функции.
Цепочка областей видимости работает как стекло с односторонним движением. Вы можете видеть снаружи, но люди снаружи не могут видеть вас.

3. Установка значения ключевого слова this

this ключевое слово относится к области, к которой относится контекст выполнения.
В глобальном контексте выполнения, за пределами какой-либо функции, это относится к глобальному объекту, который является объектом окна.
Объявление функции и переменная, инициализированная ключевым словом var, назначаются в качестве свойств и методов глобального объекта (объекта окна).

var name = 'Bishnu' точно такой же, как
window.name = 'Bishnu'
name === this.name //true

В случае контекста выполнения функции этот объект не создается. Скорее, он получает доступ к среде, в которой он определен.
В объектах это ключевое слово указывает не на глобальный контекст выполнения, а на сам объект. Ссылка на это внутри объекта будет такой же, как theObject.property/method

2. Выполнение кода

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

Стек выполнения JavaScript

Стек выполнения/стек вызовов отслеживает все контексты выполнения, созданные в течение жизненного цикла скрипта.
JavaScript – это однопоточный язык, что означает, что он может выполнять только одну задачу за один раз. один раз. Когда происходят другие действия, функции и события, для каждого из них создаются отдельные контексты выполнения, а из-за однопоточной природы js создается стек нагроможденных контекстов выполнения для выполнения, известный как выполнение. Стек.
Сначала, когда сценарии загружаются в браузер, создается глобальный контекст выполнения по умолчанию, в котором механизм js начинает выполнять код и помещается в нижнюю часть стека выполнения.
Затем механизм js ищет вызовы функций. в коде. Для каждого вызова функции создается новый контекст выполнения функции для этой функции, который помещается поверх текущего контекста выполнения.
Контекст выполнения (EC), который в данный момент находится вверху, становится активным EC и будет выполняться. сначала движком js.
Как только выполнение всего кода в активном контексте выполнения завершено, движок js извлекает EC конкретной функции из стека выполнения, перемещается к следующему под ним и так далее. на.

 var name = "Victor";
 
 function first(){
  var a = "hi";
  second();
  console.log(`&{a} ${name}`);
  }
  function second(){
  var b = "hey!";
  third();
  console.log(`&{b} ${name}`);
  }
 function third(){
  var c = "Hello";
  console.log(`${c} ${name}`);
 }

Сначала скрипт загружается в движок js. Затем движок js создает GEC и помещает его в конец стека выполнения.

Переменная name определяется вне какой-либо функции, поэтому она находится в GEC и хранится в его VO. Тот же процесс происходит и для функций.
(первая, вторая и третья)
когда движок js встречает первый вызов функции, для него создается новый FEC. Этот новый контекст помещается поверх текущего контекста, которым является GEC. На время первого вызова функции ее контекст выполнения становится активным контекстом, в котором сначала выполняется код js.

В первой функции переменная a=”hi” сохраняется в ее FEC, а не в ГЭК. Затем вторая функция вызывается внутри первой функции. Выполнение первой функции будет приостановлено из-за однопоточной природы js. Он должен дождаться своего выполнения, то есть завершения второй функции. Движок js снова создает новый FEC для второй функции и помещает его на вершину стека, делая его активным контекстом.
Переменная b="hey!" сохраняется в его FEC, а третья функция вызывается во второй функции. Его FEC создается и помещается поверх стека выполнения.

Внутри третьей функции переменная c="Hello" сохраняется в FEC, а сообщение "Hello Victor" выводится на консоль. Следовательно, третья функция выполнила свои задачи, и мы говорим, что она возвращается, ее FEC удаляется с вершины стека, а FEC второй функции снова становится активным контекстом.
Во второй функции «Эй! Виктор» входит в консоль. Функция завершает возврат задачи, и EC извлекается из стека вызовов. Затем первая функция выполняется полностью, и стек EC первой функции выталкивается из стека, поэтому управление возвращается к GEC кода.

И наконец, когда выполнение всего кода завершается, движок js удаляет GEC из текущего стека.

Асинхронное выполнение кода

JavaScript — это однопоточный язык программирования. Это означает, что JavaScript может выполнять только одну операцию в один и тот же момент времени.

Движок JavaScript выполняет скрипт из верхней части файла и работает вниз. Он создает контексты выполнения, помещает и извлекает функции из стека вызовов и из него на этапе выполнения. Если функция выполняется долго, вы не можете взаимодействовать с веб-браузером во время выполнения функции, потому что страница зависает.

Функция, выполнение которой занимает много времени, называется блокирующей функцией. Технически функция блокировки блокирует все действия на веб-странице, например щелчок мышью.

function task(message) {
    // emulate time consuming task
    let n = 10000000000;
    while (n > 0){
        n--;
    }
    console.log(message);
}

console.log('Start script...');
task('Call an API');
console.log('Done!');

/*
Start script...
Call an API.
Done!
*/

Обратные вызовы на помощь

Чтобы блокирующая функция не блокировала другие действия, вы обычно помещаете ее в функцию обратного вызова для последующего выполнения. Например:

console.log('Start script...');

setTimeout(() => {
    task('Download a file.');
}, 1000);

console.log('Done!');

/*
Start script...
Done!
Download a file.
*/

В этом примере вы сразу увидите сообщение «Запустить скрипт…» и «Готово!». И после этого вы увидите сообщение «Скачать файл».

Когда вы вызываете функцию setTimeout(), отправляете запрос на выборку или нажимаете кнопку, веб-браузер может выполнять эти действия одновременно и асинхронно.

setTimeout(), запросы на выборку и события DOM являются частью веб-API веб-браузера.

В нашем примере при вызове функции setTimeout() движок JavaScript помещает ее в стек вызовов, а веб-API создает таймер, который истекает через 1 секунду.

Затем движок JavaScript помещает функцию task() в очередь, называемую очередью обратного вызова или очередью задач:

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

Такая итерация называется тиком в цикле событий. Каждое событие — это просто обратный вызов функции.
Если стек вызовов не пуст, цикл событий ждет, пока он не станет пустым, и помещает следующую функцию из очереди обратного вызова в стек вызовов. Если очередь обратного вызова пуста, ничего не произойдет.

См. другой пример:

console.log('Hi!');

setTimeout(() => {
    console.log('Execute immediately.');
}, 0);

console.log('Bye!');

/*
Hi!
Bye!
Execute immediately.
*/

В этом примере тайм-аут равен 0 секунд, поэтому сообщение «Выполнить немедленно» должно появиться перед сообщением «Пока!». Однако это так не работает.

Движок JavaScript помещает следующий вызов функции в очередь обратного вызова и выполняет его, когда стек вызовов пуст. Другими словами, движок JavaScript выполняет его после console.log('Пока!').

Контекст выполнения JavaScript является основой для правильного понимания многих других фундаментальных концепций. Контекст выполнения (GEC и FEC) и стек вызовов — это процессы, выполняемые под капотом JS-движка, которые позволяют выполнять наш код.