В отличие от других языков, JavaScript имеет переменное продвижение, как в следующем примере кода:
console.log(x)
var x = 'hello world';
Этот код не сообщит об ошибке, он выведетundefined
. Это так называемое переменное продвижение, но для конкретных деталей того, как JS-движок обрабатывает его, вам также необходимо понимать контекст выполнения JS Execution Context.
1. Execution Context
Контекст выполнения - это контекст, когда JS выполняет код. Если выполняется функция вызова, она будет вводить контекст выполнения функции, и контекст выполнения определят такие функции, как это, переменные, объекты и определенные методы, используемые во время выполнения функции.
Когда браузер загружает скрипт, он по умолчанию напрямую входит в глобальный контекст выполнения и помещает глобальный контекст в стек. Если функция вызывается в коде, контекст выполнения функции (контекст функции) будет создан и помещен в стек вызовов, чтобы стать текущим контекстом среды выполнения. Когда функция выполняется, контекст выполнения функции извлекается из стека вызовов и возвращается к предыдущему контексту выполнения.
2. Выполните контекстную классификацию
-
Global execution context. Когда файл js загружается в браузер для запуска, он входит в глобальный контекст выполнения. Глобальные переменные находятся в этом контексте выполнения. Код доступен в любом месте.
-
Functional execution context. Контекст, определенный в конкретном методе. Доступен только в этом методе и внутри методов внутри этого метода.
-
Eval. Контекст, определенный в методе Eval. Этот метод не рекомендуется использовать без дополнительного изучения.
3. Execution Stack
Js является однопоточным выполнением и предназначен для доступа только к одному контексту выполнения за раз. Следовательно, контекст выполнения в верхней части стека вызовов будет выполняться первым, а после выполнения он вернется в верхний контекст выполнения для продолжения выполнения. Ссылка на сообщение в блогеДинамическая графикаследующим образом:
4. Подробности выполнения контекста выполнения
В контексте выполнения js-движок в основном делится на два этапа:
создать сцену(когда функция вызывается, но до ее выполнения)
-
Создать цепь
-
определить суть этого
executionContextObj = {
'scopeChain': { /* variableObject + all parent execution context's variableObject */ },
'variableObject': { /* function arguments / parameters, inner variable and function declarations */ },
'this': {}
}
ВыполнениеContextObj создается, когда функция вызывается перед запуском.Параметры аргументов на этапе создания будут переданы напрямую, а переменные, определенные внутри функции, будут инициализированы до значения undefined.
этап выполнения
- Просканируйте код еще раз, присвойте значения переменным и выполните код.
Ниже приведен псевдокод, выполняемый движком JS во время контекста выполнения.
- найти вызывающую функцию
- Перед выполнением кода функции создайте контекст выполнения
- Переходим к этапу создания:
- Инициализировать цепочку вызовов Scope Chain
- Создайте переменные объекты:
- Создайте объект arguments, инициализируйте имя и значение входной переменной.
- Поиск функций, объявленных в этом контексте выполнения:
- Для объявленной функции в объекте переменных создается соответствующее имя переменной, а ее значение указывает на функцию (функция хранится в куче)
- Если имя функции уже существует, перезапишите существующее новым эталонным значением.
- Сканировать переменные, объявленные в контексте:
- Для объявления переменной в объекте переменной также создается соответствующее имя переменной, а ее значение инициализируется неопределенным.
- Если имя переменной уже существует, пропустите его и продолжите сканирование.
- Определяет контекст, на который указывает this
- Фазы выполнения кода:
- Выполнить код в функции и присвоить значения соответствующим переменным (переменным, этап создания которых не определен)
Простой пример выглядит следующим образом:
console.log(foo(22))
console.log(x);
var x = 'hello world';
function foo(i) {
var a = 'hello';
var b = function privateB() {
};
function c() {
}
console.log(i)
}
(a): код сначала входит в фазу создания глобального контекста.
ExecutionContextGlobal = {
scopeChain: {...},
variableObject: {
x: undefined,
foo: pointer to function foo()
},
this: {...}
}
Затем войдите в фазу выполнения глобального контекста выполнения. На этом этапе код выполняется один за другим сверху вниз, переходя кconsole.log(foo(22))
Во время этой строки фаза создания уже присвоила значение foo в variableObject, поэтому она будет выполняться при выполненииfoo(22)
функция.
при исполненииfoo(22)
Когда функция, она войдетfoo()
, см. (b) для деталей.
при выполнении кconsole.log(x)
когда, в это времяx
Присвоить значение в variableObject какundefined
, поэтому распечатываетundefined
, что точнопеременное продвижениедал результаты.
при выполнении кvar x = 'hello world';
, x в variableObject присваивается какhello world
.
продолжать внизfoo
объявление функции, таким образом, ничего не делая, и фаза выполнения заканчивается. Ниже представлен ExecutionContextGlobal после завершения фазы выполнения.
ExecutionContextGlobal = {
scopeChain: {...},
variableObject: {
x: 'hello world',
foo: pointer to function foo()
},
this: {...}
}
(B): при вызове foo js (22) в контексте выполнения функции foo (), первый этап созданного контекста.
ExecutionContextFoo = {
scopeChain: {...},
variableObject: {
arguments: {
0: 22,
length: 1
},
i: 22,
c: pointer to function c()
a: undefined,
b: undefined
},
this: {...}
}
После запуска фазы выполнения ExecutionContextfoo выглядит следующим образом.
fooExecutionContext = {
scopeChain: { ... },
variableObject: {
arguments: {
0: 22,
length: 1
},
i: 22,
c: pointer to function c()
a: 'hello',
b: pointer to function privateB()
},
this: { ... }
}
После выяснения контекста выполнения в JS легко понять, что происходит с продвижением переменных. Перед выполнением кода контекст выполнения уже присвоил соответствующее объявление, но переменная присваивается какundefined
, функция присваивает соответствующую ссылку, а затем присваивает соответствующее значение переменной на этапе выполнения.
5. Различайте объявления функций и выражения функций
Сначала посмотрите на следующие фрагменты кода, что получится на выходе?
Question 1:
function foo(){
function bar() {
return 3;
}
return bar();
function bar() {
return 8;
}
}
alert(foo());
Question 2:
function foo(){
var bar = function() {
return 3;
};
return bar();
var bar = function() {
return 8;
};
}
alert(foo());
Question 3:
alert(foo());
function foo(){
var bar = function() {
return 3;
};
return bar();
var bar = function() {
return 8;
};
}
Question 4:
function foo(){
return bar();
var bar = function() {
return 3;
};
var bar = function() {
return 8;
};
}
alert(foo());
Приведенные выше 4 фрагмента кода выводятся соответственно8
,3
,3
,[Type Error: bar is not a function]
.
function name([param,[, param,[..., param]]]) { [statements] }
объявление функции с ключевым словомfunction
Определите функцию в начале и одновременно укажите определенное имя функции. Такие, как самый простой каштан:
function bar() {
return 3;
}
Через контекст выполнения функции объявление функции даетhoisted, то есть объявление функции будет перемещено в начало кода.
bar:pointer to the function bar()
, Потому что есть объявлениеbar()
функции, поэтому более поздние определения переопределяют более ранние.
var myFunction = function [name]([param1[, param2[, ..., paramN]]]) { statements };
В выражении функции имя функции можно не указывать, простой каштан выглядит следующим образом:
//anonymous function expression
var a = function() {
return 3;
}
//named function expression
var a = function bar() {
return 3;
}
//self invoking function expression
(function sayHello() {
alert("hello!");
})();
Вышеприведенные три - все функциональные выражения, а последняя является немедленным выполнением функции. Выражения функций не поднимаются к верхней части кода, как под вопросом 2, во время этапа создания контекста выполнения функций, в Foo.vobar : undefined
, назначение выполняется на этапе выполнения.
Возвращаясь к вопросу 4:
function foo(){
return bar(); // 执行阶段返回调用bar(),但创建阶段bar被赋值为 undefined,所以报Type Error。
var bar = function() {
return 3;
};
var bar = function() {
return 8;
};
}
alert(foo());
Ссылаться на
What is the Execution Context & Stack in JavaScript?