Область действия JavaScript — одна из самых запутанных вещей в языке. Я пишу это, чтобы улучшить свое собственное понимание, если это поможет другим, то мне будет приятно прийти сюда, так что давайте вернемся в наш мир воображения. В JavaScript есть два правила области видимости: одна лексическая область видимости, а другая для краткости мы можем сказать, динамическая область видимости или «эта» область видимости. Я попытаюсь визуализировать и объяснить, как они работают под капотом и связанные с ними другие языковые конструкции.

Состояние и область действия JavaScript

Возможность языка присваивать значение переменной, которая будет храниться где-то в памяти, и позже мы можем получить доступ, изменить или использовать эту переменную, когда нам нужно дать программе состояние или это состояние переменной.

Теперь о том, как область видимости работает в JavaScript. Область видимости — это набор четко определенных правил о том, как получить доступ к определенной переменной, которая уже объявлена ​​из разных мест, что означает видимость переменной из функции.

Модель выполнения JavaScript

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

Исходный код — › Лексирование/токенизация — › Парсинг — › Генерация кода

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

  1. Фаза создания:
    а. Создание переменного объекта (VO)
    b. Создание цепочки Scope.
    c. Разрешение «этого» значения
  2. Этап выполнения:
    а. Код функции, сгенерированной из текущего контекста выполнения, выполняется построчно.

Переменный объект (VO)

  1. Создается объект аргумента, содержащий все аргументы, которые были переданы в функцию.
  2. Код сканируется на наличие объявлений функций: для каждой функции в объекте Variable создается свойство, указывающее на функцию [Подъем]
  3. Код сканируется на наличие объявлений переменных: для каждой переменной в объекте Variable создается свойство, которому присваивается значение undefined. [Подъем]

Глобальный контекст выполнения

  • Код, который не находится внутри какой-либо функции
  • Связан с глобальным объектом
  • В браузере это объект окна

Теперь соедините все это вместе, когда мы запускаем JavaScript, он сначала разрешает все аргументы для функции, затем объявляет функцию и присваивает значение объявлению переменной, создает цепочку области видимости и, наконец, разрешает значение в область «эта».
Переменный объект (VO) — › Цепочка областей действия — › Присвоение значения this

Подъем переменных и функций:

Движок JavaScript сначала обрабатывает код, затем выполняет его, переменная и функция обрабатываются, а затем выполняются, поэтому мы можем вызывать перед ее объявлением. подъем не работает над выражением функции. поэтому, когда мы объявляем
var name = 'Привет!';
движок интерпретирует это следующим образом
var name;
name = 'Привет!';
Поднимаются только сами объявления, а любые присваивания или другая
исполняемая логика остаются на месте. Также важно отметить, что подъем выполняется для каждой области видимости. Это приводит к тому, что все объявления в области видимости, независимо от того, где они появляются, сначала обрабатываются до выполнения самого кода. Сами объявления поднимаются, но присваивания, даже присваивания функциональных выражений, не поднимаются. Будьте осторожны с повторяющимися объявлениями, особенно смешанными между обычными объявлениями var и объявлениями функций — если вы это сделаете, вас ждет опасность!

Объем блока:

Блокировать область в сокрытии информации за пределами области. Раньше это было возможно только через функцию. После ES6 есть ключевые слова «let» и «const», к которым прикрепляется объявление переменной области действия, в которой она содержится. Мы не можем получить доступ к переменной «bar» или «baz» вне «{}».

ReferenceError связан с ошибкой разрешения области, в то время как TypeError подразумевает, что разрешение области прошло успешно, но с результатом была предпринята недопустимая/невозможная операция.

Закрытие и объем:

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

Замыкание — это когда функция может запомнить и получить доступ к своей лексической области видимости, даже если она вызывается за пределами своей лексической области видимости. Шаблон модуля закрытия требует двух ключевых характеристик:
1) внешняя функция-оболочка вызывается для создания объемлющей области
2) возвращаемое значение функции-оболочки должно включать ссылку по крайней мере на одну внутреннюю функцию, которая затем имеет замыкание над частной внутренней областью оболочки.

Лексическая область видимости в JavaScript:

Лексическая область видимости — это набор правил о том, как Engine может искать переменную
и где она ее находит. Ключевой характеристикой лексической области видимости является то, что она определяется во время написания кода автором.

  • Область видимости отвечает на вопрос «где мы можем получить доступ к определенной переменной?»
  • Каждая новая функция создает область: пространство/среду, в которой доступны определяемые ею переменные.
  • Лексическая область действия: функция, которая лексически находится внутри другой функции, получает доступ к области видимости внешней функции.

Хотя стек и область действия выглядят одинаково, на самом деле они разные.

Результат выполнения не перехвачен. ReferenceError: c не определен. Из-за области действия третьей функции она может обращаться только к переменным a и d.

JavaScript «эта» область видимости

Чтобы понять привязку this, мы должны понять call-site: место в коде, где функция вызывается (а не там, где она объявлена). Мы должны осмотреть сайт вызова, чтобы ответить на вопрос: на что это ссылка?
Поиск сайта вызова, как правило, это: «иди и найди, откуда вызывается функция», но это не всегда так просто, как некоторые шаблоны кодирования могут скрыть истинное место вызова. Что важно, так это подумать о стеке вызовов (стеке функций, которые были вызваны, чтобы привести нас к текущему моменту выполнения). Интересующий нас сайт вызова находится в вызове перед текущей выполняемой функцией. Теперь обратим внимание на то, как call-site определяет, куда будет указывать this во время выполнения функции.

Вы должны проверить сайт вызова и определить, какое из четырех правил применяется
1. Привязка по умолчанию (вызов автономной функции)
2. Неявная привязка
3. Явная привязка
4. Жесткий переплет

Все по порядку: Порядок старшинства
Жесткая привязка › Явная привязка › Неявная привязка › Привязка по умолчанию

Лексический this (функция стрелки)

Стрелочная функция вводит поведение, называемое «лексическим this». Короткое объяснение заключается в том, что стрелочные функции ведут себя совсем не так, как обычные функции, когда дело доходит до «этой» области видимости. Он отбрасывает все обычные правила связывания «этого» и значения их непосредственной лексической объемлющей области видимости, чем бы она ни была.

Определение «это»

Ключевое отличие: лексическая область видимости — это время записи, тогда как динамическая область видимости (и
это!) — время выполнения. Лексической области видимости важно, где была объявлена ​​функция, а динамической области видимости важно, откуда функция была вызвана. Наконец: this
заботится о том, как была вызвана функция, что показывает, насколько тесно связан механизм с идеей динамической области видимости.

1. Если функция вызывается с новой (новой привязкой)? Если это так, это будет вновь созданный объект.
var bar = new Foo()

2. Если функция, вызываемая с помощью привязки, вызывает жесткую привязку? Если это так, «это» или применить (явная привязка), даже скрытый внутри a
является явно указанным объектом.
var bar = foo.call( obj2 )

3. Если функция вызывается с контекстом (неявная привязка), иначе известный как владеющий или содержащий объект? Если это так, то это объект контекста.
var bar = obj1.foo()

4. В противном случае по умолчанию выберите этот глобальный параметр (привязка по умолчанию). Если в строгом режиме, выберите неопределенный объект.
var bar = foo()

Ключевое слово «это» на практике

Ресурсы:
1. http://ryanmorr.com/understanding-scope-and-context-in-javascript/
2. http://dannyzhang.run/2017/04/03 /Как-JavaScript-работает-за-кулисами/
3. https://www.html5rocks.com/en/tutorials/internals/howbrowserswork/
4. https:/ /github.com/getify/You-Dont-Know-JS
5. http://thibaultlaurens.github.io/javascript/2013/04/29/how-the-v8-engine-works/
6. https://javascript.info/browser-environment
7. https://saegeullee.com/the-complete-javascript-course-section3-how-javascript-works -за кулисами/
8. https://flaviocopes.com/javascript-event-loop/