Говоря о контексте выполнения JavaScript с помощью продвижения переменных

внешний интерфейс JavaScript
Говоря о контексте выполнения JavaScript с помощью продвижения переменных

В отличие от других языков, 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 является однопоточным выполнением и предназначен для доступа только к одному контексту выполнения за раз. Следовательно, контекст выполнения в верхней части стека вызовов будет выполняться первым, а после выполнения он вернется в верхний контекст выполнения для продолжения выполнения. Ссылка на сообщение в блогеДинамическая графикаследующим образом:

execution stack

4. Подробности выполнения контекста выполнения

В контексте выполнения js-движок в основном делится на два этапа:

создать сцену(когда функция вызывается, но до ее выполнения)

  • Создать цепь

  • определить суть этого

executionContextObj = {
    'scopeChain': { /* variableObject + all parent execution context's variableObject */ },
    'variableObject': { /* function arguments / parameters, inner variable and function declarations */ },
    'this': {}
}

ВыполнениеContextObj создается, когда функция вызывается перед запуском.Параметры аргументов на этапе создания будут переданы напрямую, а переменные, определенные внутри функции, будут инициализированы до значения undefined.

этап выполнения

  • Просканируйте код еще раз, присвойте значения переменным и выполните код.

Ниже приведен псевдокод, выполняемый движком JS во время контекста выполнения.

  1. найти вызывающую функцию
  2. Перед выполнением кода функции создайте контекст выполнения
  3. Переходим к этапу создания:
    • Инициализировать цепочку вызовов Scope Chain
    • Создайте переменные объекты:
      • Создайте объект arguments, инициализируйте имя и значение входной переменной.
      • Поиск функций, объявленных в этом контексте выполнения:
        • Для объявленной функции в объекте переменных создается соответствующее имя переменной, а ее значение указывает на функцию (функция хранится в куче)
        • Если имя функции уже существует, перезапишите существующее новым эталонным значением.
      • Сканировать переменные, объявленные в контексте:
        • Для объявления переменной в объекте переменной также создается соответствующее имя переменной, а ее значение инициализируется неопределенным.
        • Если имя переменной уже существует, пропустите его и продолжите сканирование.
    • Определяет контекст, на который указывает this
  4. Фазы выполнения кода:
    • Выполнить код в функции и присвоить значения соответствующим переменным (переменным, этап создания которых не определен)

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

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?

Execution context, Scope chain and JavaScript internals

JavaScript. The core.