JavaScript | Контекст Выполнения — efim360.ru

JavaScript | Контекст Выполнения

Говорить о Контексте Выполнения (Execution Context) нужно как можно раньше, при изучении языка JavaScript. Это фундаментальное понятие, которое открывает глаза на суть Объектно-Ориентированного Программирования. Но это всё звучит по-прежнему страшно и непонятно. Давайте снизим градус "Высшего Образования" и перейдём на "Кухонный Уровень".

 

Пример из обычной жизни

Представьте себе такую ситуацию. Встретились два приятеля и разговаривают между собой. Одного зовут Вася, а другого Петя.

Вася спрашивает:

"Петруха, когда мы поедем с тобой париться ко мне в баню?"

Петя отвечает:

"Я сейчас еду на заправку, покупаю для машины бензина на полный бак и мы мчим с вениками к тебе в баню."

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

 

Если я сейчас вас спрошу, какую (чью) машину собирается заправить Петя? Мне кажется что у вас с ответом не должно возникнуть проблем. Мы с вами понимаем, что Петя будет заправлять СВОЮ машину. Петину машину, не Васину. То есть Петя заправляет Петину машину.

И вот на этом месте давайте проанализируем, как так получилось. Мы поняли, что машина Петина, потому что упоминал о заправке машины сам Петя.

Так как Петя явно не говорил, что поедет заправлять машину соседа Коли, то мы сделали логичный вывод, что машина принадлежит самому Пете и её заправлять будет он сам.

В реальной жизни мы с вами очень часто опускаем информацию об Объекте Контекста (Context Object). Если речь идёт о чём-то "НАШЕМ" (принадлежащему нам самим), то мы вообще об этом не говорим. Это экономит слова, а значит и общую длительность диалогов с кем бы то ни было. В реальной жизни так проще. Петя поедет заправлять СВОЮ машину. Пусть это будет чёрный Мерседес 2022 года выпуска.

 

Теперь самое время подключить к объяснению язык JavaScript. Согласитесь, что любой автовладелец знает как заправлять автомобиль. Он знает как заправлять ЛЮБОЙ автомобиль, а не только СВОЙ. То есть в классе "Человек-Автомобилист" есть некоторая функция, которая знает какие нужно произвести действия, чтобы в автомобильный бак поступило нужное топливо.

В этой функции меняется только "Объект Автомобиль", который будет заправлять наш экземпляр "Человек-Автомобилист". Алгоритм функции универсален, а значит его можно использовать с любым экземпляром "Человек-Автомобилист". Осталось только понять каким образом можно нацеливать выполнение функции на взаимодействие с ЭТИМ конкретным "Объектом Автомобиль".

 

Когда Петя на заправке возьмёт в руки топливный пистолет, тогда он воткнёт его в СВОЙ автомобиль (в ЭТОТ), а не в соседний. Петя в момент вызова функции "Заправить Бак" чётко идентифицирует цель заправки. Иными словами, функция "Заправить Бак" имеет чёткий/конкретный Объект Контекста Выполнения (Execution Context Object). Заправка бака осуществляется в контексте конкретного автомобиля "чёрный Мерседес 2022 года выпуска".

let mers2022 = {

    model: "Mers",

    year: 2022,

    color: "Black",

    fuel_tank_capacity: 70,

    current_fuel: 20,

    fill_the_fuel_tank: function(){this.current_fuel=this.fuel_tank_capacity; console.log("Бак заправлен у автомобиля!", this)}

}

Скриншот из консоли браузера:

У объекта объявили функциональное свойство fill_the_fuel_tank с ключевыми словами this - JavaScript
У объекта объявили функциональное свойство fill_the_fuel_tank с ключевыми словами this - JavaScript

Вызываем метод:

mers2022.fill_the_fuel_tank()

В результате бак заправится и в консоль выведется информация о том объекте, который был заправлен.

 

В JavaScript мы не можем опустить информацию об Объекте Контекста (Context Object). Мы не можем промолчать. Чтобы получить нужную нам цель мы используем ключевое слово this.

Слово this в момент определения контекста выполнения говорит Стеку Контекста Выполнения (Execution Context Stack) о каком объекте контекста идёт речь. В результате код в теле функции знает над каким объектом нужно проводить преобразования.

 

Функции JavaScript не живут сами по себе

Из всего выше перечисленного следует то, что любая функция в JavaScript "контекстуально" принадлежит какому-то объекту. Сама по себе она не существует.

Если функция объявлена на самом верхнем уровне видимости через ключевое слово function, то её Объектом Контектса будет ГЛОБАЛЬНЫЙ объект. В среде выполнения браузера этим ГЛОБАЛЬНЫМ объектом является объект Window. Имя функции станет ключом нового свойства у объекта Window, а значением будет сама функция с инструкцией на выполнения кода, взятого из тела этой функции. Просто объект Window становится больше на одно свойство. И если в теле функции будет использовано зарезервированное слово this, то оно будет ссылаться на ГЛОБАЛЬНЫЙ объект Window.

Когда функция вызывается, тогда Стек определяет её Объект Контекста. Почему это так? Почему это происходит только в момент вызова функции? Всё дело в том, что одна функция может вызывать какую-то другую функцию внутри себя. Это значит, что родительская функция войдёт в Стек раньше, но выйти из него сможет только после выполнения дочерней функции. Поэтому если не поменять Контекст Выполнения, то логика вложенной функции может внести правки не в тот объект ... и произойдёт катастрофа. Это похоже на то, как если бы вы пытались надеть носки на голову. Функция "Надевания носков" должна вызываться только у объектов "Ноги" и никак иначе.

 

Строгий режим - Use Strict

Ситуация с глобальным объектом может измениться если мы будем использовать директиву "use strict"

 

 

Информационные ссылки

ECMAScript | Контексты Выполнения

Стандарт ECMAScript - Раздел "9.4 Execution Contexts" - https://tc39.es/ecma262/#sec-execution-contexts 

Поделись записью