Meshbeyn / JavaScript

Конспект по Javascript

4 Управление ходом выполнения скрипта

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

Блоки и прерывающие команды break и continue

Многие управляющие команды ограничивают свое действие следующей командой. Например, следующий if ограничивает выполнение только команды i++:

if(a == 0)
   i++;
a = a + b;

Если необходимо, чтобы команда управляла несколькими командами, их необходимо заключить в фигурные скобки {}. Такая конструкция называется операторный блок и структурно рассматривается как одна команда.

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

Команда break

Команда break прекращает выполнение блока. Она полезна в ситуациях, когда необходимый результат достигнут и дальнейшее выполнение команд цикла бесполезно при поиске или вредно при вычислениях.

Команда continue

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

Условные команды if, ?: и switch

Условные команды выполняют или не выполняют блок кода в зависимости от выполнения условия. Самой простой условной командой является команда if.

Команда if

if(условие)
{
    блок кода для true
}
else
{
    блок кода для false
}

Оператор if выполняет два разных блока кода в зависимости от выполнения условия. Если условие равнозначно true, выполняется первый блок. В противном случае выполняется блок кода после ключевого слова else.

Часто альтернативный блок не нужен, поэтому блок else необязателен. При вложении условных операторов блок else всегда принадлежит последнему if без else. Такое вложение может запутать, поэтому желательно при вложении всегда использовать блочные скобки.

Тернарный оператор ?:

Тернарный оператор уже рассматривался в статье про операторы. Он позволяет сокращать часто требующуюся операцию по использованию разных значений в зависимости от условия. Поскольку он является оператором, его можно использовать напрямую в выражениях без лишних переменных.

Команда switch

switch(условие)
{
   case константа1:
        команды
   case константа2:
        команды
   ...
   default:
        команды
}

Команда switch используется для случаев, когда выполняются разные действия в зависимости от некоего выражения. Эта команда соответствует цепочке команд if, которые сравнивают одно и то же выражение с разными константами. Команда вычисляет выражение в заголовке и ищет совпадение в блоках case. Как только совпадение найдено, начинают выполняться команды из этого блока. Если в конце блока не поставить break, выполнение будет продолжено в следующем блоке. Это частая логическая ошибка, однако в некоторых случаях такое поведение именно то, что нужно. Особенно часто это используется, когда для нескольких констант нужно выполнить одно действие.

Блок default необязателен. Он будет вызван, если ни один из блоков case не подошел.

Циклы for, for in, while, do while

Циклы позволяют выполнять один блок кода несколько раз в зависимости от выполнения условия. Вы можете рассматривать их как бесконечную цепочку из команд if с одним условием. Такая цепочка перестает выполняться только когда условие перестает быть верным.

Цикл while

while(условие)
{
  блок кода
}

Цикл while является самым простым циклом и представляет собой именно бесконечную цепочку одинаковых команд if.

Цикл do while

do
{
  блок кода
}
while(условие);

Цикл do while проверяет условие не до выполнения блока, а после выполненной работы. Он соответствует конструкции из цикла while, перед которым записан его же блок кода. Обычно этот цикл используется, когда тело цикла в любом случае должно быть выполнено хотя бы один раз. Другие случаи использования сложны и редки.

Цикл for

for(инициализация; условие; обновление)
{
  блок кода
}

Цикл for - это самый универсальный и используемый цикл. Порядок выполнения такого цикла:

  1. Инициализация. Обычно создание и инициализация используемых переменных.
  2. Условие. Проверяется выполнение условия. Если оно не выполняется, происходит выход из цикла.
  3. Блок кода. Выполняются команды из внутреннего блока (тела цикла).
  4. Обновление. Обычно происходит обновление управляющих переменных.
  5. Переходим к пункту 2

Цикл for можно представить при помощи цикла while:

инициализация
while(условие)
{
        блок кода

        обновление
}

Пример использования:

for(var i = 0; i < 10; i++)
{
  Sum += Arr[i];
}

Этот код добавляет к переменной Sum первые десять элементов массива Arr.

Цикл for in

for(переменная in объект или массив)
{
   блок кода
}

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

Функции

Вызов функции также позволяет изменить ход выполнения программы. Функция - это блок кода с именем и параметрами. В отличие от остальных способов управления ходом скрипта, использование функций делится на два этапа: объявление и вызов. Поскольку вызов функции отделен от ее объявления, достаточно объявить ее один раз и вызывать потом сколько нужно. При вызове функции управление переходит в ее блок, но после выполнения функции, управление продолжается с команды, следующей за вызовом функции. Когда интерпретатор встречает определение функции, он создает для нее имя и вырезает из кода скрипта. Тело функции выполняется только в результате команды вызова. Определения глобальных функций могут сильно мешать читаемости кода, поэтому желательно группировать их в начале или конце скрипта. Пример определения функции с именем Left, возвращающей левые n символов строки S:

function Left(S, n)
{
    var t = S.substr(0, n);
    return t;
}

Команда вызова функции состоит из имени функции, круглых скобок и списка параметров. Количество аргументов при вызове должно совпадать с количеством параметров в декларации функции. Имена аргументов из заголовка функции могут использоваться как локальные переменные. Можно создавать дополнительные переменные командой var. В функции также можно использовать глобальные переменные. Частой ошибкой является назначение несуществующей переменной без ключевого слова var. При этом используется или автоматически создается глобальная переменная. Все локальные переменные функции видимы только внутри ее блока и автоматически уничтожаются после выхода.

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

JavaScript позволяет создание функций с одинаковым именем, но разным количеством аргументов (перегрузка по количеству аргументов). Однако перегрузки по типам аргументов нет, так как переменные могут свободно менять тип и заголовок функции типы аргументов никак не ограничивает. Перегрузка по типам иногда эмулируется внутри функции выполнением разного кода в зависимости от типов аргументов.

Если функция возвращает значение (имеет команду return c возвращаемым значением), ее вызов можно использовать в выражениях как любой другой оператор.

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

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

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

(function(a,b,c) 
{ 
   var d = b * b - 4 * a * c; 
   if(d < 0)
      alert("{}");
   else if(d == 0)
      alert("{" + -b / 2 / a + "}");
   else
      alert("{" + (-b + Math.sqrt(d))/ 2 / a + "; " + (-b - Math.sqrt(d)) / 2 / a + "}");
})(1, 2, 1);

Здесь определяется функция без имени и сразу же вызывается с параметрами a = 1, b = 2, c = 1. Функция вычисляет корни квадратного уравнения, выводит результат "{-1}" и сразу же уничтожается. Такая запись усложняет чтение скрипта для новичков и выполняется даже дольше, чем нормальная функция. В коротких скриптах от нее нет никакой пользы. Однако, в больших сложных системах она борется с замусориванием глобального пространства. Объявление большого количества переменных и функций приводит к длительным поискам нужного объекта во время выполнения.