- Оригинальный адрес:Understanding Execution Context and Execution Stack in Javascript
- Адрес перевода:Понимание контекста выполнения Javascript и стека выполнения
- Переводчик:Ся Цзяхао
- Корректор:Шэн Синьюань,Тан Вэй,Ши Лэй,Чжан Ченгронг
Если вы разработчик JavaScript или хотите им стать, то вы должны знать внутренний механизм выполнения программ JavaScript. Понимание контекста выполнения и стеков выполнения также помогает понять другие концепции JavaScript, такие как подъем, область действия и замыкания.
Правильное понимание концепций контекста выполнения и стека выполнения поможет вам стать лучшим разработчиком JavaScript.
Без дальнейших церемоний, давайте перейдем к делу.
что такое контекст выполнения
Короче говоря, контекст выполнения — это абстракция среды, в которой анализируется и выполняется текущий код JavaScript.Любой код, работающий в JavaScript, выполняется в контексте выполнения.
тип контекста выполнения
Всего существует три типа контекстов выполнения:
-
Глобальный контекст выполнения:Это самый простой контекст выполнения по умолчанию. Код, которого нет ни в одной функции, находится в глобальном контексте выполнения. Он делает две вещи: 1. Создает глобальный объект, который в браузере является объектом окна. 2. будет
this
Указатель на этот глобальный объект. В программе может существовать только один глобальный контекст выполнения. - Контекст выполнения функции:Каждый раз, когда вызывается функция, для этой функции создается новый контекст выполнения. Каждая функция имеет свой собственный контекст выполнения, но он создается только при вызове функции. В программе может существовать любое количество контекстов выполнения функций. Всякий раз, когда создается новый контекст выполнения, он выполняет серию шагов в определенном порядке, обсуждаемом далее в этой статье.
-
Контекст выполнения функции Eval:Бег
eval
Код в функции также получает свой собственный контекст выполнения, но поскольку функция eval обычно не используется разработчиками Javascript, она не будет здесь обсуждаться.
стек выполнения
Стек выполнения, также известный как стек вызовов в других языках программирования, имеет структуру LIFO (последний пришел — первый обслужен), в которой хранятся все контексты выполнения, созданные во время выполнения кода.
Когда механизм JavaScript впервые читает ваш сценарий, он создает глобальный контекст выполнения и помещает его в текущий стек выполнения. Всякий раз, когда происходит вызов функции, механизм создает новый контекст выполнения для функции и помещает его на вершину текущего стека выполнения.
Движок запустит функцию, контекст выполнения которой находится на вершине стека выполнения.Когда функция будет завершена, ее соответствующий контекст выполнения будет извлечен из стека выполнения, а элемент управления контекстом будет перемещен в следующий контекст выполнения стека. текущий стек выполнения.
Давайте разберемся с этим на следующем примере кода:
let a = 'Hello World!';
function first() {
console.log('Inside first function');
second();
console.log('Again inside first function');
}
function second() {
console.log('Inside second function');
}
first();
console.log('Inside Global Execution Context');
Когда приведенный выше код загружается в браузер, механизм JavaScript создает глобальный контекст выполнения и помещает его в текущий стек выполнения. при звонкеfirst()
функция, механизм JavaScript создает новый контекст выполнения для функции и помещает ее на вершину текущего стека выполнения.
когда вfirst()
вызов функцииsecond()
функция, механизм Javascript создает новый контекст выполнения для функции и помещает ее на вершину текущего стека выполнения. когдаsecond()
После выполнения функции ее контекст выполнения извлекается из текущего стека выполнения, а управление контекстом будет перемещено в следующий контекст выполнения текущего стека выполнения, а именноfirst()
Контекст выполнения функции.
когдаfirst()
После выполнения функции ее контекст выполнения извлекается из текущего стека выполнения, а управление контекстом перемещается в глобальный контекст выполнения. Как только весь код выполнен, механизм Javascript удаляет глобальный контекст выполнения из стека выполнения.
Как создается контекст выполнения
До сих пор мы видели, как механизм JavaScript управляет контекстом выполнения, теперь давайте разберемся, как механизм JavaScript создает контекст выполнения.
Контекст выполнения создается в два этапа:1) этап создания; 2) Этап исполнения
создать сцену
Перед выполнением любого кода JavaScript контекст выполнения находится в фазе создания. Всего на этапе создания происходит три вещи:
- Конечноthisзначение, также известное какThis Binding.
- Лексическая средаКомпоненты созданы.
- Переменная средаКомпоненты созданы.
Следовательно, контекст выполнения можно представить концептуально следующим образом:
ExecutionContext = {
ThisBinding = <this value>,
LexicalEnvironment = { ... },
VariableEnvironment = { ... },
}
This Binding:
В глобальном контексте выполненияthis
Значение указывает на глобальный объект в браузере,this
Значение указывает на объект окна.
В контексте выполнения функцииthis
Значение зависит от того, как была вызвана функция. Если он вызывается со ссылкой на объект, тоthis
Значение устанавливается для этого объекта, в противном случаеthis
Значение устанавливается на глобальный объект илиundefined
(в строгом режиме). Например:
let person = {
name: 'peter',
birthYear: 1994,
calcAge: function() {
console.log(2018 - this.birthYear);
}
}
person.calcAge();
// 'this' 指向 'person', 因为 'calcAge' 是被 'person' 对象引用调用的。
let calculateAge = person.calcAge;
calculateAge();
// 'this' 指向全局 window 对象,因为没有给出任何对象引用
Лексическое окружение
Официальный ES6Документация определяет лексическую среду как:
Лексическая среда — это тип спецификации, определяющий связь идентификаторов с конкретными переменными и функциями на основе лексически вложенной структуры кода ECMAScript. Лексическое окружение состоит из записи окружения и внешнего лексического окружения, которое может быть нулевой ссылкой.
Короче говоря, лексическая среда – это содержащаяСопоставление переменной идентификатораСтруктура. (здесьидентификаторпредставляет имя переменной/функции,Переменнаяявляется ссылкой на фактический объект (включая объекты функционального типа) или примитивные значения)
В лексическом окружении есть два компонента: (1)запись окружающей среды(2)Ссылка на внешнюю среду
- Экологические записиэто фактическое место, где хранятся объявления переменных и функций.
- Ссылка на внешнюю средуОзначает, что он имеет доступ к своему внешнему лексическому окружению.
Лексическое окружениеЕсть два типа:
-
глобальная среда(в глобальном контексте выполнения) — это лексическая среда без внешней среды. Ссылка на внешнюю среду глобальной среды:null. Он имеет глобальный объект (объект окна) и связанные с ним методы и свойства (такие как методы массива) и любые пользовательские глобальные переменные,
this
Значение указывает на этот глобальный объект. -
Функциональная среда, пользовательские переменные в функции хранятся вЭкологические записисередина. Ссылка на внешнюю среду может быть глобальной средой или внешней функциональной средой, содержащей внутреннюю функцию.
Уведомление:зафункциональная средаС точки зрения,Экологические записитакже содержитarguments
объект, который содержит сопоставление между индексом и параметрами, переданными в функцию, и параметрами, переданными в функциюдлина (количество). Например, следующая функцияarguments
Объект выглядит так:
function foo(a, b) {
var c = a + b;
}
foo(2, 3);
// arguments 对象
Arguments: {0: 2, 1: 3, length: 2},
Экологические записиЕсть также два типа (показаны ниже):
- Декларативные экологические записиХраните переменные, функции и параметры. Функциональная среда содержит записи декларативной среды.
- запись среды объектаИспользуется для определения ассоциаций переменных и функций, которые появляются в глобальном контексте выполнения. Глобальная среда содержит записи объектной среды.
Абстрактно говоря, лексическое окружение в псевдокоде выглядит так:
GlobalExectionContext = {
LexicalEnvironment: {
EnvironmentRecord: {
Type: "Object",
// 标识符绑定在这里
outer: <null>
}
}
FunctionExectionContext = {
LexicalEnvironment: {
EnvironmentRecord: {
Type: "Declarative",
// 标识符绑定在这里
outer: <Global or outer function environment reference>
}
}
Переменная среда:
Это также лексическая среда,EnvironmentRecord
содержит поVariableStatementsПривязки, созданные в этом контексте выполнения.
Как упоминалось выше, переменное окружение также является лексическим окружением, поэтому оно обладает всеми свойствами лексического окружения, определенными выше.
В ES6,LexicalEnvironmentкомпоненты иVariableEnvironmentКомпоненты отличаются тем, что первый используется для хранения объявлений функций и переменных (let
иconst
) привязки, которые используются только для хранения переменных (var
) привязка.
Давайте объединим несколько примеров кода, чтобы понять вышеприведенные концепции:
let a = 20;
const b = 30;
var c;
function multiply(e, f) {
var g = 20;
return e * f * g;
}
c = multiply(20, 30);
Контекст выполнения выглядит так:
GlobalExectionContext = {
ThisBinding: <Global Object>,
LexicalEnvironment: {
EnvironmentRecord: {
Type: "Object",
// 标识符绑定在这里
a: < uninitialized >,
b: < uninitialized >,
multiply: < func >
}
outer: <null>
},
VariableEnvironment: {
EnvironmentRecord: {
Type: "Object",
// 标识符绑定在这里
c: undefined,
}
outer: <null>
}
}
FunctionExectionContext = {
ThisBinding: <Global Object>,
LexicalEnvironment: {
EnvironmentRecord: {
Type: "Declarative",
// 标识符绑定在这里
Arguments: {0: 20, 1: 30, length: 2},
},
outer: <GlobalLexicalEnvironment>
},
VariableEnvironment: {
EnvironmentRecord: {
Type: "Declarative",
// 标识符绑定在这里
g: undefined
},
outer: <GlobalLexicalEnvironment>
}
}
Уведомление:только когда встречается функцияmultiply
Контекст выполнения функции создается только при вызове функции.
вы могли заметитьlet
иconst
Определенная переменная не имеет никакого связанного с ней значения, ноvar
Определенная переменная устанавливается вundefined
.
Это связано с тем, что на этапе создания код сканируется и анализируется на наличие объявлений переменных и функций, где объявления функций хранятся в среде, а для переменных устанавливаются значенияundefined
(существуетvar
случае) или оставаться неинициализированным (вlet
иconst
в случае).
Вот почему вы можете получить доступ до объявленияvar
определенная переменная (хотя онаundefined
), но при доступе до объявленияlet
иconst
Определенная переменная подскажет причину ошибки ссылки.
Это то, что мы называем подъемом переменных.
этап выполнения
Это самая легкая часть всей статьи. На этом этапе выполняются присваивания всем переменным и, наконец, выполняется код.
Примечание:На этапе выполнения, если механизм Javascript не может найти фактическое местоположение, объявленное в исходном кодеlet
значение переменной, то ей будет присвоеноundefined
ценность.
Суммировать
Мы обсудили внутреннее выполнение JavaScript. Хотя вам не нужно изучать все эти концепции, чтобы стать хорошим разработчиком JavaScript, понимание приведенных выше концепций поможет вам легче и глубже понять другие концепции, такие как подъем, домены и замыкания.