Существует ли прекомпиляция в JS? Как работает это «поведение»?

JavaScript

предисловие

Прекомпиляция, как следует из названия, заключается в выполнении некоторой работы по замене текста кода. Это первая работа, которую необходимо выполнить во всем процессе компиляции. Библия JavaScript — MDN прямо говорит, что JavaScript — это интерпретируемый, а не компилируемый язык, поэтому для JS не существует предварительной компиляции. Но людям нравится называть это предварительной компиляцией, и это не повредит.Ключ в том, почему люди думают, что в JS есть предварительная компиляция, и какое поведение JS заставляет людей думать, что он производит предварительную компиляцию? Давайте быстро поболтаем.

представлять

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

  1. Причастие. состоит в том, чтобы разделить код на атомарные символы (токен)
  2. Перевести синтаксический анализ токенов в AST (Spanning Tree синтаксиса).

Второй этап анализа

3. Когда встречается оператор объявления, объявление будет передано в область видимости (scope) для создания привязки, выделения памяти и установки переменной в значение undefined или тела функции по умолчанию.
Затем вы можете выполнить код.Каждый раз, когда во время выполнения встречается присваивание или значение, привязка будет искаться из области видимости. Глядя на это с этой стороны, может быть, оно немного «предварительно скомпилировано»? Но на самом деле правильнее будет назвать это предварительной обработкой. Далее давайте взглянем на волну операций, которая больше всего похожа на прекомпиляцию — галантерею третьего шага.

«Предварительно скомпилированный»

Мой учитель рассказал мне эту шутку, прежде чем рассказать мне о соответствующих знаниях:

Я завис во время интервью из-за этого вопроса

var a = 100
function foo(){
    console.log(a)
}
foo()

В: Почему значение вывода равно 100?
Ответ: Поскольку 100 присваивается a.

После того, как учитель закончил говорить, у меня закружилась голова, и я вообще не понял смысла шутки, а позже понял, что это исследование знаний о «прекомпиляции».
В целом, «предварительно скомпилированные» можно разделить на

  1. Создание объекта GO (глобальный объект) происходит при загрузке страницы.
  2. Создание объекта АО (объекта активации) происходит непосредственно перед выполнением функции.

Конкретные шаги заключаются в следующем:

全局预编译
1. 创建GO对象
2. 找变量声明,将变量声明作为GO对象的属性名,并赋值undefined
3. 找全局里的函数声明,将函数名作为GO对象的属性名,值赋予函数体

局部预编译
1. 创建一个AO对象
2. 找形参和变量声明,将形参和变量声明作为AO对象的属性名,值为undefined
3. 将实参和形参统一
4. 在函数体里找函数声明,将函数名作为AO对象的属性名,值赋予函数体

Так что на вопрос интервью в этом анекдоте следует ответить так:

首先,编译器创建一个GO对象
找到变量声明 var a
和函数声明 function foo(){}
将上面两个变量声明作为GO的属性名赋初值
GO{
    a:undefined
    foo:function(){}
}
然后运行第一行代码 a=100
在GO中将100赋值给a
再执行第五行代码运行foo函数
创建一个AO对象
在函数体内找变量声明和形参,(无)
再在函数体内找函数声明(无)
所以
AO{
    
}
完成后运行第三行代码,输出a
先在AO对象中寻找a的值,发现不存在,向外部作用域扩展,在GO对象中寻找a,发现a的值为100
输出100

Конечно, вопрос в анекдоте слишком простой, но он позволяет наглядно понять ход этой "прекомпиляции"
Теперь давайте рассмотрим упрощенную версию вопроса для собеседования и потренируем свои навыки:

global = 100
    function fn() {
      console.log(global); 
      global = 200
      console.log(global); 
      var global = 300
    }
    fn()

Какова его логика и вывод? Благодаря пошаговому анализу мы можем узнать, что конкретный анализ должен быть таким:

GO: {
      global: undefined => 100,
      fn: function() {}
    }
    global = 100 // 没有声明的变量默认为全局变量,也会放到GO中
    function fn() {
      console.log(global); // 输出undefined
      global = 200
      console.log(global); // 输出200
      var global = 300
    }
    AO: {
      global: undefined => 200 => 300
    }
    fn()

На этом статья заканчивается, надеюсь, вам всем понравилось. Если есть какие-либо ошибки, добро пожаловать в область комментариев для получения рекомендаций.