<MyRusakov.ru />

Программирование на C++ в Unreal Engine 5

Программирование на C++ в Unreal Engine 5

Данный курс научит Вас созданию игр на C++ в Unreal Engine 5. Курс состоит из 12 разделов, в которых Вас ждёт теория и практика. Причём, в качестве практики будет создан весьма крупный проект объёмом свыше 5000 строк качественного кода, который уже на практике познакомит Вас с принципами создания игр на C++ в Unreal Engine 5.

Параллельно с курсом Вы также будете получать домашние задания, результатом которых станет, в том числе, полноценная серьёзная работа для портфолио.

Помимо самого курса Вас ждёт ещё и очень ценный Бонус: «Тестирование Unreal-проектов на Python», в рамках которого Вы научитесь писать очень полезные тесты для тестирования самых разных аспектов разработки игр.

Подробнее
Подписка

Подпишитесь на мой канал на YouTube, где я регулярно публикую новые видео.

YouTube Подписаться

Подписавшись по E-mail, Вы будете получать уведомления о новых статьях.

Подписка Подписаться

Добавляйтесь ко мне в друзья ВКонтакте! Отзывы о сайте и обо мне оставляйте в моей группе.

Мой аккаунт Мой аккаунт Моя группа
Опрос

Какая тема Вас интересует больше?

Что такое замыкание в javascript.

Что такое замыкание в javascript.

Всем привет! В этой статье мы рассмотрим, что такое замыкание в javascript.

Это довольно простая тема, но она требует понимания. Для начала давайте рассмотрим, что происходит внутри функции.

function greeting(name) {
  // LexicalEnvironment = {name: 'Николай', text: undefined}
  var text = 'Здравствуйте, ' + name;
  // LexicalEnvironment = {name: 'Николай', text: 'Здравствуйте, Николай'}
  alert(text);
}

greeting('Николай');

Что здесь происходит и что такое LexicalEnvironment? Давайте разберемся.

Когда функция вызывается, у нее создается объект LexicalEnvironment, в который записываются все локальные переменные и функции, а также ссылка на внешнюю область видимости(об этом позже). В нашем случае у нас есть локальная переменная name, у которой сразу есть значение(то, которое мы передаем) и это "Николай". В одной из статей я уже писал, однако напомню, что интерпретатор все знает про все переменные заранее. Именно по этому у нас в самом начале функции уже есть переменная text, интерпретатор знает про нее, но так как мы еще не дошли по присваивания этой переменной какого-то значения, то она равна undefined. Теперь мы присваиваем переменной значение, и наш объект LexicalEnvironment меняется. Его свойство text становится равным тому, что мы записали("Здравствуйте, Николай" в нашем случае). После того, как функция отработала, объект LexicalEnvironment уничтожается. При последующих вызовах функции он будет создан снова и т.д.

Теперь перейдем к следующему примеру. Скажите, что будет выведено в этом случае?

var b = 2;
function x(a) {
  alert(a + b);
}
x(1);

Подумали? Думаю, большинство ответило, что будет выведено число 3, и это правильный ответ, однако можете вы рассказать, как интерпретатор узнал о переменной b? Ведь ее нет в теле функции. Если нет, давайте разбираться.

На самом деле в javascript есть скрытое свойство, которое называется [[Scope]]. Когда функция объявляется, то она всегда объявляется где-то. Эта функция может быть в другой функции, может быть в глобальном объекте и т.д. В нашем случае функция объявлена в глобальном объекте window, поэтому свойство x.[[Scope]] = window.

Дальше будем рассматривать на примере кода с комментариями.

var b = 2;
function x(a) { // x.[[Scope]] = window
  // LexicalEnvironment = {a: 1} -> window
  alert(a + b);
}
x(1);

Эта стрелочка у объекта LexicalEnvironment - это ссылка на внешнюю область видимости, и эта ссылка устанавливается по свойству [[Scope]]. Таким образом в объекте LexicalEnvironment у нас будет ссылка на внешний объект window. Когда интерпретатор ищет переменную, то он сначала ищет ее в объекте LexicalEnvironment, затем, если он не нашел переменную, то он смотрим в ссылку, переходит во внешнюю область видимости и ищет ее там и так до конца. Если он нигде этой переменной не нашел, то будет ошибка. В нашем случае переменную a интерпретатор возьмет из объекта LexicalEnvironment, а переменную b из объекта window. Конечно, если у нас будет локальная переменная b с каким-то значением, то она запишется в объект LexicalEnvironment и впоследствии будет взята оттуда, а не из внешней области видимости.

ВАЖНО! Запомните, что свойство [[Scope]] устанавливается по тому месту, где функция была объявлена, а не вызвана, именно поэтому код ниже выведет число 3, а не 5, как некоторые могли подумать.

bar b = 2;
function x(a) {
  alert(a + b);
}

function y() {
  var b = 4;
  x(1);
}

y();

Это все была прелюдия только для того, чтобы вы поняли, как это все работает, и вам было легче понять, как работают замыкания. А теперь перейдем непосредственно к теме статьи.

Как я уже говорил, объект LexicalEnvironment уничтожается каждый раз после выполнения функции и создается снова при повторном вызове. Однако что, если мы хотим сохранить эти данные? Т.е. мы хотим, чтобы все, что записано в LexicalEnvironment сейчас, сохранилось и было использовано при следующих вызовах? Именно для этого и существуют замыкания.

function greeting(name) {
  // LexicalEnvironment = {name: 'Николай'}
  return function() { // [[Scope]] = LexicalEnvironment
   alert(name);
  };
}

var func = greeting('Николай');
greeting = null;
func();

Давайте посмотрим, что мы сделали. Сначала мы создаем функцию greeting, в которую передается имя. В функции создается объект LexicalEnvironment, где создается свойство(наша локальная переменная) name и ей присваивается имя "Николай". А теперь важно: мы возвращаем из функции другую функцию, внутри которой выводим через alert переменную name. Дальше мы присваиваем переменной func значение, возвращенное из функции greeting, а это значение - наша функция, которая выводит имя. Теперь мы greeting присваиваем null, т.е. мы просто уничтожаем нашу функцию greeting, однако, когда мы вызовем func, то увидим значение переменной name("Николай") функции greeting. Как такое возможно, скажете вы? А очень просто. Все дело в том, что наша возвращаемая функция также имеет свойство [[Scope]], которое ссылается на внешнюю область видимости, а эта внешняя область видимости в нашем случае - объект LexicalEnvironment нашей функции greeting. Поэтому, несмотря на то, что мы удалили нашу функцию greeting, объект LexicalEnvironment не удалился и остался в памяти, и он будет оставаться в памяти до тех пор, пока на него будет хотя бы одна ссылка. У нас эта ссылка - наша возвращаемая функция, которая использует переменную name этого объекта LexicalEnvironment.

Итак, давайте теперь дадим определение тому, что такое замыкание.

Замыкание - функция вместе со всеми переменными, которые ей доступны.

Что же, статья получилась довольно объемная, но это только потому, что я попытался как можно подробнее описать весь процесс работы замыкания. На закрепление хочу привести простой пример - счетчик с использованием только что изученной темы. Пожалуйста, разберитесь с кодом и напишите в комментариях, как и почему он работает. Если вы чего-то не поняли, вы также можете задать вопрос. Спасибо за внимание!

function makeCounter() {
  var currentCount = 0;

  return function() {
   currentCount++;
   return currentCount;
  };
}

var counter = makeCounter();
counter();
counter();
alert(counter()); // 3

Копирование материалов разрешается только с указанием автора (Михаил Русаков) и индексируемой прямой ссылкой на сайт (http://myrusakov.ru)!

Добавляйтесь ко мне в друзья ВКонтакте: http://vk.com/myrusakov.
Если Вы хотите дать оценку мне и моей работе, то напишите её в моей группе: http://vk.com/rusakovmy.

Если Вы не хотите пропустить новые материалы на сайте,
то Вы можете подписаться на обновления: Подписаться на обновления

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

Порекомендуйте эту статью друзьям:

Если Вам понравился сайт, то разместите ссылку на него (у себя на сайте, на форуме, в контакте):

  1. Кнопка:

    Она выглядит вот так: Как создать свой сайт

  2. Текстовая ссылка:

    Она выглядит вот так: Как создать свой сайт

  3. BB-код ссылки для форумов (например, можете поставить её в подписи):

Комментарии (5):

demoparadise demoparadise 07.03.2015 04:46:35

А какое прикладное применение у этих замыканий? Что они упрощают или какие преимущества дают?

Ответить

php_programmer php_programmer 09.03.2015 15:05:04

Замыкание - это то, благодаря чему, по сути, язык программирования javascript вообще существует. Любое использование функций порождает замыкание. Основной их плюс в том, что вы можете хранить значения переменных на длительное время, а не сразу уничтожать их, как только функция закончит свою работу, и то, что вы можете из функции обратиться к переменной, которая находится в области видимости на уровень выше. Если научиться это правильно использовать, то это открывает большие возможности. Если вы не совсем понимаете этой темы, то советую сначала оставить ее, изучить другие основы javascript, а потом прочитать книжку "JavaScript. Шаблоны" от автора Стоян Стефанов. Там вы сможете не только изучить шаблоны, но также увидите, как и где применять замыкания.

Ответить

demoparadise demoparadise 09.03.2015 15:07:49

Только начал знакомиться с js, рано мне еще такие темы изучать, спасибо.

Ответить

Сомнение Сомнение 21.12.2016 09:58:30

Цитата из статьи : " ВАЖНО! Запомните, что свойство [[Scope]] устанавливается по тому месту, где функция была объявлена, а не вызвана, именно поэтому код ниже выведет число 3, а не 5, как некоторые могли подумать." Переставим запуск функции сразу после объявления переменной b и получим результат NaN. Может всё-таки место запуска функции тоже влияет на [SCOPE]? Вот код - y(); var b = 2; function x(a) { alert(a + b); } function y() { var b = 4; x(1); } В статье код надо подправить - bar на var поменять.

Ответить

Anteya Anteya 03.02.2023 12:29:28

Это лучшее объяснение замыкания, что я встречала на просторах интернета!

Ответить

Для добавления комментариев надо войти в систему.
Если Вы ещё не зарегистрированы на сайте, то сначала зарегистрируйтесь.