Javascript прерывание цикла. Циклы. Примеры использования for

Что отступы можно считать показателем сложности кода (хотя и довольно грубым). Сами по себе отступы нейтральны, так как они – лишь средство форматирования текста, но всё дело в том, что они используются для выделения особых блоков программ, например – управляющих конструкций. Читая код и натыкаясь на отступ, программист вынужден принимать во внимание то, на что указывает отступ, держать в памяти контекст, в котором существует выделенный блок. Это, естественно, повторяется, если в выделенном отступами участке кода появляется ещё один особый фрагмент.

Если не обращать внимание на содержание текстов, то вот как обычно выглядит сложный код, участки которого похожи на лежащие на боку буквы «V», и простой код, блок которого, если не учитывать разную длину строк, похож на прямоугольник.


Чем больше отступов – тем сложнее обычно и код

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

Возьмём, например, массивы. Традиционно для их обработки используют различные виды циклов. Понятия «массив» и «цикл» неразрывно связаны в сознании многих программистов. Однако цикл – конструкция весьма неоднозначная. Вот что пишет о циклах Луис Атенцио в книге «Функциональное программирование в JavaScript »: «Цикл – это жёсткая управляющая конструкция, которую нелегко использовать повторно и сложно состыковать с другими операциями. Кроме того, использование циклов означает появление кода, который меняется с каждой итерацией.»


Можно ли избавиться от циклов?

Цикл – одна из основных структурных управляющих конструкций, и, собственно, мы не собираемся говорить о том, что циклы – это зло, от которого надо избавляться. Наша главная цель заключается в снижении сложности собственного кода за счёт минимального использования циклов при обработке массивов. Возможно ли это? Предлагаем выяснить вместе.

Циклы

Мы уже говорили о том, что управляющие конструкции, вроде циклов, усложняют код. Но почему это так? Взглянем на то, как работают циклы в JavaScript.

В JS существует несколько способов организации циклов. В частности, один из базовых видов циклов – это while . Прежде чем вникать в подробности, немного подготовимся. А именно – создадим функцию и массив, с которым будем работать.

// oodlify:: String -> String function oodlify(s) { return s.replace(//g, "oodle"); } const input = [ "John", "Paul", "George", "Ringo", ];
Итак, имеется массив, каждый элемент которого мы собираемся обработать с помощью функции oodlify . Если использовать для решения этой задачи цикл while , получится следующее:

Let i = 0; const len = input.length; let output = ; while (i < len) { let item = input[i]; let newItem = oodlify(item); output.push(newItem); i = i + 1; }
Обратите внимание на то, что мы, для того, чтобы отслеживать текущий обрабатываемый элемент массива, используем счётчик i . Необходимо инициализировать его нулём и увеличивать на единицу в каждой итерации цикла. Кроме того, нужно сравнивать его с длиной массива, с len , для того, чтобы знать, когда надо прекратить работу.

Этот шаблон настолько распространён, что в JavaScript имеется более простой способ организовать подобную конструкцию – цикл for . Такой цикл позволит решить ту же задачу следующим образом:

Const len = input.length; let output = ; for (let i = 0; i < len; i = i + 1) { let item = input[i]; let newItem = oodlify(item); output.push(newItem); }
Цикл for – полезная конструкция, так как благодаря ей все стандартные вспомогательные операции со счётчиком выносятся в верхнюю часть блока. Используя while , легко забыть о необходимости инкрементировать счётчик i , что приведёт к запуску бесконечного цикла. Определённо, цикл for гораздо удобнее цикла while . Но давайте притормозим и взглянем на то, чего пытается достичь наш код. Мы хотим обработать, с помощью функции oodlify() , каждый элемент массива и поместить то, что получилось, в новый массив. Сам по себе счётчик, используемый для доступа к элементам массива, нас не интересует.

Подобный шаблон работы с массивами, предусматривающий выполнение неких действий с каждым элементом, весьма распространён. В результате, в ES2015 появилась новая конструкция цикла, которая позволяет забыть о счётчике. Это – цикл for…of . В каждой итерации такого цикла предоставляется следующий элемент массива. Выглядит это так:

Let output = ; for (let item of input) { let newItem = oodlify(item); output.push(newItem); }
Код выглядит гораздо чище. Обратите внимание на то, что тут нет ни счётчика, ни операции сравнения. При таком подходе даже не нужно обращаться к конкретному элементу массива по индексу. Цикл for…of берёт на себя все вспомогательные операции.

Если завершить на этом исследование способов работы с массивами и применять циклы for…of везде вместо циклов for , это уже будет неплохим шагом вперёд за счёт упрощения кода. Но… мы можем пойти дальше.

Трансформация массивов

Цикл for…of выглядит гораздо чище, чем цикл for , но и с ним в коде имеется немало вспомогательных элементов. Так, надо инициализировать массив output и в каждой итерации цикла вызывать метод push() . Код можно сделать ещё более компактным и выразительным, но прежде чем этим заняться, давайте немного расширим демонстрационную задачу. Как быть, если с помощью функции oodlify() надо обработать два массива?

Const fellowship = [ "frodo", "sam", "gandalf", "aragorn", "boromir", "legolas", "gimli", ]; const band = [ "John", "Paul", "George", "Ringo", ];
Вполне очевидное решение – использовать два цикла и обработать массивы в них:

Let bandoodle = ; for (let item of band) { let newItem = oodlify(item); bandoodle.push(newItem); } let floodleship = ; for (let item of fellowship) { let newItem = oodlify(item); floodleship.push(newItem); }
Вполне рабочий вариант. А код, который работает – это гораздо лучше, чем код, который не решает поставленную перед ним задачу. Но два очень похожих фрагмента кода не особенно хорошо согласуются с принципом разработки ПО DRY . Код можно подвергнуть рефакторингу для того, чтобы снизить число повторений.

Следуя этой идее, создаём такую функцию:

Function oodlifyArray(input) { let output = ; for (let item of input) { let newItem = oodlify(item); output.push(newItem); } return output; } let bandoodle = oodlifyArray(band); let floodleship = oodlifyArray(fellowship);
Выглядит это гораздо лучше, но что, если имеется ещё одна функция, с помощью которой мы тоже хотим обрабатывать элементы массивов?

Function izzlify(s) { return s.replace(/+/g, "izzle"); }
Теперь функция oodlifyArray() не поможет. Однако, если создать ещё одну подобную функцию, на этот раз – izzlufyArray() , мы опять повторимся. Всё же, создадим такую функцию и сравним её с oodlifyArray() :

Function oodlifyArray(input) { let output = ; for (let item of input) { let newItem = oodlify(item); output.push(newItem); } return output; } function izzlifyArray(input) { let output = ; for (let item of input) { let newItem = izzlify(item); output.push(newItem); } return output; }
Эти две функции невероятно похожи. Может быть, обобщим шаблон, которому они следуют? Наша цель заключается следующем: «Имеются массив и функция. Нужно получить новый массив, в который будут записаны результаты обработки каждого из элементов исходного массива с помощью функции». Подобный способ обработки массивов называют «отображением» или «трансформацией» (mapping в англоязычной терминологии). Функции, которые выполняют подобные операции, обычно называют «map». Вот как выглядит наш вариант такой функции:

Function map(f, a) { let output = ; for (let item of a) { output.push(f(item)); } return output; }
Хотя цикл теперь и вынесен в отдельную функцию, совсем избавиться от него не получилось. Если пойти до конца и попытаться обойтись совсем без циклических конструкций, можно написать рекурсивный вариант того же самого:

Function map(f, a) { if (a.length === 0) { return ; } return .concat(map(f, a.slice(1))); }
Рекурсивное решение выглядит весьма элегантно. Всего пара строчек кода и минимум отступов. Но рекурсивные реализации алгоритмов обычно используют с большой осторожностью, кроме того, они отличаются плохой производительностью в старых браузерах. И, на самом деле, нам совершено не нужно самим писать функцию реализации операции отображения, если только на то нет веской причины. То, что делает наша функция map – задача настолько распространённая, что в JavaScript имеется встроенный метод map() . Если воспользоваться этим методом, код окажется вот таким:

Let bandoodle = band.map(oodlify); let floodleship = fellowship.map(oodlify); let bandizzle = band.map(izzlify); let fellowshizzle = fellowship.map(izzlify);
Обратите внимание на то, что здесь вообще нет ни отступов, ни циклов. Конечно, при обработке данных, где-то в недрах JavaScript, могут использоваться циклы, но это уже – не наша забота. Теперь код получился и сжатым, и выразительным. Кроме того, он проще.

Почему этот код проще? Может показаться, что вопрос это глупый, но подумайте об этом. Он проще потому что он короче? Нет. Компактность кода – это не признак простоты. Он проще потому что при таком подходе мы разбили задачу на части. А именно, есть две функции, которые работают со строками: oodlify и izzlify . Эти функции не должны ничего знать о массивах или о циклах. Имеется ещё одна функция – map , которая работает с массивами. При этом ей совершенно безразлично, какого типа данные в массиве, или то, что именно мы хотим с этими данными делать. Она просто исполняет любую переданную ей функцию, передавая ей элементы массива. Вместо того, чтобы всё смешивать, мы разделили обработку строк и обработку массивов. Именно поэтому итоговый код оказался проще.

Свёртка массивов

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

Рассмотрим пример. Предположим, имеется список объектов, каждый из которых представляет супергероя:

Const heroes = [ {name: "Hulk", strength: 90000}, {name: "Spider-Man", strength: 25000}, {name: "Hawk Eye", strength: 136}, {name: "Thor", strength: 100000}, {name: "Black Widow", strength: 136}, {name: "Vision", strength: 5000}, {name: "Scarlet Witch", strength: 60}, {name: "Mystique", strength: 120}, {name: "Namora", strength: 75000}, ];
Надо найти самого сильного героя. Для того, чтобы это сделать, можно воспользоваться циклом for…of:

Let strongest = {strength: 0}; for (let hero of heroes) { if (hero.strength > strongest.strength) { strongest = hero; } }
Учитывая все обстоятельства, этот код не так уж и плох. Мы обходим в цикле массив, храня объект самого сильного из просмотренных героев в переменной strongest . Для того, чтобы яснее увидеть шаблон работы с массивом, представим, что ещё надо выяснить общую силу всех героев.

Let combinedStrength = 0; for (let hero of heroes) { combinedStrength += hero.strength; }
В каждом из этих двух примеров имеется рабочая переменная, которую инициализируют перед запуском цикла. Затем, в каждой итерации, обрабатывают один элемент массива и обновляют переменную. Для того, чтобы выделить схему работы ещё лучше, вынесем операции, выполняемые внутри циклов, в функции, и переименуем переменные для того, чтобы подчеркнуть схожесть производимых действий.

Function greaterStrength(champion, contender) { return (contender.strength > champion.strength) ? contender: champion; } function addStrength(tally, hero) { return tally + hero.strength; } const initialStrongest = {strength: 0}; let working = initialStrongest; for (hero of heroes) { working = greaterStrength(working, hero); } const strongest = working; const initialCombinedStrength = 0; working = initialCombinedStrength; for (hero of heroes) { working = addStrength(working, hero); } const combinedStrength = working;
Если всё переписано так, как показано выше, два цикла оказываются очень похожими. Единственное, что их отличает – это вызываемые в них функции и начальные значения переменных. В обоих циклах массив сворачивается до одного значения. В англоязычной терминологии такая операция называется «reducing». Поэтому создадим функцию reduce , реализующую обнаруженный шаблон.

Function reduce(f, initialVal, a) { let working = initialVal; for (let item of a) { working = f(working, item); } return working; }
Надо отметить, что, как и в случае с шаблоном функции map , шаблон функции reduce распространён так широко, что JavaScript предоставляет его в качестве встроенного метода массивов. Поэтому свой метод, если на это нет особой причины, писать не нужно. С использованием стандартного метода код будет выглядеть так:

Const strongestHero = heroes.reduce(greaterStrength, {strength: 0}); const combinedStrength = heroes.reduce(addStrength, 0);
Если присмотреться к итоговому результату, можно обнаружить, что получившийся код ненамного короче того, что был раньше, экономия совсем невелика. Если бы мы использовали функцию reduce , написанную самостоятельно, то, в целом, код получился бы больше. Однако, наша цель заключается не в том, чтобы писать короткий код, а в том, чтобы уменьшать его сложность. Итак, уменьшили ли мы сложность программы? Могу утверждать, что уменьшили. Мы отделили код циклов от кода, который обрабатывает элементы массива. В результате отдельные участки программы стали более независимыми. Код получился проще.

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

Фильтрация массивов

Итак, имеется функция map для выполнения операций над каждым элементом массива. Есть функция reduce , которая позволяет сжать массив до единственного значения. Но что, если нужно извлечь из массива лишь некоторые его элементы? Для того, чтобы эту идею исследовать, расширим список супергероев, добавим туда некоторые дополнительные данные:

Const heroes = [ {name: "Hulk", strength: 90000, sex: "m"}, {name: "Spider-Man", strength: 25000, sex: "m"}, {name: "Hawk Eye", strength: 136, sex: "m"}, {name: "Thor", strength: 100000, sex: "m"}, {name: "Black Widow", strength: 136, sex: "f"}, {name: "Vision", strength: 5000, sex: "m"}, {name: "Scarlet Witch", strength: 60, sex: "f"}, {name: "Mystique", strength: 120, sex: "f"}, {name: "Namora", strength: 75000, sex: "f"}, ];
Теперь предположим, что имеется две задачи:

  1. Найти всех героев-женщин.
  2. Найти всех героев, сила которых превышает 500.
К решению этих задач вполне можно подойти, используя старый добрый цикл for…of:

Let femaleHeroes = ; for (let hero of heroes) { if (hero.sex === "f") { femaleHeroes.push(hero); } } let superhumans = ; for (let hero of heroes) { if (hero.strength >= 500) { superhumans.push(hero); } }
В общем-то, выглядит это вполне пристойно. Но тут невооруженным глазом виден повторяющийся шаблон. На самом деле, циклы совершенно одинаковые, различаются они только блоками if . Что если вынести эти блоки в функции?

Function isFemaleHero(hero) { return (hero.sex === "f"); } function isSuperhuman(hero) { return (hero.strength >= 500); } let femaleHeroes = ; for (let hero of heroes) { if (isFemaleHero(hero)) { femaleHeroes.push(hero); } } let superhumans = ; for (let hero of heroes) { if (isSuperhuman(hero)) { superhumans.push(hero); } }
Функции, которые возвращают только true или false иногда называют предикатами. Мы используем предикат для принятия решения о том, надо ли сохранить в новом массиве очередное значение из массива heroes .

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

Function filter(predicate, arr) { let working = ; for (let item of arr) { if (predicate(item)) { working = working.concat(item); } } return working; } const femaleHeroes = filter(isFemaleHero, heroes); const superhumans = filter(isSuperhuman, heroes);
Здесь, как и в случае со встроенными функциями map и reduce , в JavaScript имеется то же самое, что мы тут написали, в виде стандартного метода filter объекта Array . Поэтому писать собственную функцию, без явной необходимости, не нужно. С использованием стандартных средств код будет выглядеть так:

Const femaleHeroes = heroes.filter(isFemaleHero); const superhumans = heroes.filter(isSuperhuman);
Почему такой подход гораздо лучше, чем использование цикла for…of ? Подумайте о том, как этим можно воспользоваться на практике. У нас имеется задача вида: «Найти всех героев, которые…». Как только выяснилось, что можно решить задачу с использованием стандартной функции filter , работа упрощается. Всё, что нужно – сообщить этой функции, какие именно элементы нас интересуют. Делается это через написание одной компактной функции. Не нужно заботиться ни об обработке массивов, ни о дополнительных переменных. Вместо этого мы пишем крошечную функцию-предикат и задача решена.

И, как и в случае с другими функциями, которые работают с массивами, использование filter позволяет выразить больше информации в меньшем объёме кода. Не нужно читать весь стандартный код цикла для того, чтобы разобраться в том, что именно мы фильтруем. Вместо этого всё, что нужно понять, описано прямо при вызове метода.

Поиск в массивах

Фильтрация – весьма полезная операция. Но что, если надо найти лишь одного супергероя из списка? Скажем, нас интересует Black Widow. Функцию filter вполне можно использовать для решения этой задачи:

Function isBlackWidow(hero) { return (hero.name === "Black Widow"); } const blackWidow = heroes.filter(isBlackWidow);
Главная проблема тут заключается в том, что подобное решение не отличается эффективностью. Метод filter просматривает каждый элемент массива. Однако, известно, что в массиве лишь одного героя зовут Black Widow, а это значит, что можно остановиться после того, как этот герой найден. В то же время, функциями-предикатами удобно пользоваться. Поэтому напишем функцию find , которая найдёт и вернёт первый подходящий элемент:

Function find(predicate, arr) { for (let item of arr) { if (predicate(item)) { return item; } } } const blackWidow = find(isBlackWidow, heroes);
Тут, опять же, надо сказать, что в JavaScript есть встроенная функция, которая делает в точности то, что нужно:

Const blackWidow = heroes.find(isBlackWidow);
В итоге, как и прежде, удалось выразить нашу идею более сжато. С использованием встроенной функции find , задача поиска определённого элемента сводится к одному вопросу: «По какому признаку можно определить, что искомый элемент обнаружен?» При этом не надо беспокоиться о деталях.

О функциях reduce и filter

Читатели заметили, что неэффективно дважды проходиться по списку героев в вышеприведённых примерах к функциям reduce и filter . Использование оператора расширения (spread operator) из ES2015 позволяет удобно скомбинировать две функции, занятых свёрткой массива, в одну. Вот изменённый фрагмент кода, который позволяет проходиться по массиву лишь один раз:

Function processStrength({strongestHero, combinedStrength}, hero) { return { strongestHero: greaterStrength(strongestHero, hero), combinedStrength: addStrength(combinedStrength, hero), }; } const {strongestHero, combinedStrength} = heroes.reduce(processStrength, {strongestHero: {strength: 0}, combinedStrength: 0});
Не могу не заметить, что эта версия будет немного сложнее, чем та, в которой по массиву проходились дважды, но, если массив огромен, сокращение числа проходов по нему может оказаться весьма кстати. В любом случае, порядок сложности алгоритма остаётся O(n).

Итоги

Полагаю, представленные здесь функции – это отличный пример того, почему продуманно выбранные абстракции и пользу приносят, и в коде выглядят хорошо. Допустим, что мы используем встроенные функции везде, где это возможно. В каждом случае получается следующее:
  1. Мы избавляемся от циклов, что делает код более сжатым и, скорее всего, более лёгким для чтения.
  2. Используемый шаблон описывается с использованием подходящего имени стандартного метода. То есть – map , reduce , filter , или find .
  3. Масштаб задачи уменьшается. Вместо самостоятельного написания кода для обработки массива, нужно лишь указать стандартной функции на то, что надо с массивом сделать.
Обратите внимание на то, что в каждом случае для решения задачи используются компактные чистые функции.

На самом деле, если обо всём этом задуматься, можно прийти к выводу, который, в первый момент, кажется удивительным. Оказывается, если использовать лишь четыре вышеописанных шаблона обработки массивов, можно убрать из JS-кода практически все циклы. Ведь что делается в практически каждом цикле, написанном на JavaScript? В нём либо обрабатывается, либо конструируется некий массив, либо делается и то и другое. Кроме того, в JS есть и другие стандартные функции для работы с массивами, вы вполне можете изучить их самостоятельно. Избавление от циклов практически всегда позволяет уменьшить сложность программ и писать код, который легче читать и поддерживать.

Уважаемые JavaScript-разработчики, а у вас на примете есть стандартные функции, которые позволяют улучшить код, избавившись от каких-нибудь распространённых «самописных» конструкций?

Теги: Добавить метки

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

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

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

Делается это с помощью циклов .

Цикл while

Цикл while будет выполняться до тех пор, пока верно (истинно) выражение, переданное ему параметром. Смотрите синтаксис:

While (пока выражение истинно) { выполняем этот код циклически; в начале каждого цикла проверяем выражение в круглых скобках } /* Цикл закончится, когда выражение перестанет быть истинным. Если оно было ложным изначально - то он не выполнится ни разу! */

В принципе, цикл while может выполняться бесконечно (но это приведет к зависанию скрипта!), достаточно передать ему выражение, которое никогда не станет ложным . Например, так:

Давайте последовательно выведем с помощью цикла while числа от одного до пяти:

Var i = 0; //счетчик цикла while (i < 5) { /* С помощью оператора ++ увеличиваем i на единицу при каждом проходе цикла. */ i++; alert(i); }

Обратите внимание на переменную i – она является так называемым счетчиком цикла . Счетчики используются для того, чтобы подсчитывать, сколько раз выполнился цикл. Кроме того, они выполняют вспомогательную роль - в нашей задаче мы использовали счетчик, чтобы вывести цифры от 1 до 5.

Для счетчиков принято использовать буквы i , j и k .

Цикл for

Цикл for является альтернативой while . Он более сложен для понимания, но чаще всего его любят больше, чем while за то, что он занимает меньше строчек.

For (начальные команды; условие окончания цикла; команды после прохода цикла) { тело цикла }

Начальные команды - это то, что выполнится перед стартом цикла. Они выполнятся только один раз. Обычно там размещают начальные значения счетчиков, пример: i = 0 .

Условие окончания цикла - пока оно истинное , цикл будет работать, пример: i .

Команды после прохода цикла - это команды, которые будут выполнятся каждый раз при окончании прохода цикла. Обычно там увеличивают счетчики, например: i++ .

Давайте с помощью цикла for выведем последовательно числа от 0 до 9:

/* В начале цикла i будет равно нулю, цикл будет выполнятся пока i < 10, после каждого прохода к i прибавляется единица: */ for (var i = 0; i < 10; i++) { alert(i); //выведет 0, 1, 2... 9 }

Цикл без тела

Фигурные скобки в циклах можно не указывать - в этом случае цикл выполнит только одну строку под ним (не рекомендую так делать, часто приводит к ошибкам):

For (var i = 0; i < 10; i++) //<--- точки с запятой нет alert(i); //выведет 0, 1, 2... 9

А вот если после ) поставить точку с запятой - цикл закроется и следующая строка в него не попадет, получится так называемый цикл без тела, который в нашем случае просто прокрутится и в результате изменит значение переменной i :

For (var i = 0; i < 10; i++); //<--- точка с запятой есть alert(i); //выведет 9

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

Несколько команд в цикле for

Если нам необходимо выполнить несколько команд в круглых скобках - указываем их через запятую:

For (var i = 0, j = 2; i

Давайте разберем приведенный цикл: до прохода цикла выполнятся две команды: var i = 0, j = 2 (обратите внимание на то, что var тут пишется один раз), а после каждой итерации - целых три: i++, j++, i = i + j .

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

Цикл for для массивов

С помощью цикла for можно последовательно перебрать элементы массива. Делается это следующим образом:

<= arr.length-1; i++) { alert(arr[i]); //выведет 1, 2, 3, 4, 5 }

Ключевым моментом является то, что мы делаем перебор от нуля до длины массива минус 1 (так как номер последнего элемента массива на единицу меньше его длины).

Можно не отнимать единицу, а место <= сделать < :

Var arr = ; for (var i = 0; i < arr.length; i++) { alert(arr[i]); //выведет 1, 2, 3, 4, 5 }

Цикл for-in

Для перебора объекта используется так называемый цикл for-in . Давайте посмотрим, как он работает.

Пусть у нас дан такой объект:

Var obj = {Коля: 200, Вася: 300, Петя: 400};

Давайте выведем его ключи. Для этого используем такую конструкцию: for (key in obj) , где obj - это объект, который мы перебираем, а key - это переменная, в которую последовательно будут ложится ключи объекта (ее название может быть любым, какое придумаете - такое и будет).

То есть в данном цикле не надо указывать условие окончания - он будет перебирать ключи объекта, пока они не закончатся.

Итак, вот так мы выведем все ключи объекта (по очереди):

Var obj = {Коля: 200, Вася: 300, Петя: 400}; for (key in obj) { alert(key); //выведет "Коля", "Вася", "Петя" }

Если нам нужны не ключи, а значения, нужно обратиться к нашему объекту по ключу, вот так: obj .

Как это работает: в переменной key сначала будет "Коля", то есть obj в данном случае все равно, что obj["Коля"] , при следующем проходе цикла в переменной key будет "Вася" и так далее.

Итак, выведем все элементы объекта:

Var obj = {Коля: 200, Вася: 300, Петя: 400}; for (key in obj) { alert(obj); //выведет 200, 300, 400 }

Инструкция break

Иногда нам необходимо прервать выполнение цикла досрочно, в случае с циклом for это значит до того, как цикл переберет все элементы массива.

Зачем такое может понадобится? Например, перед нами стоит задача выводить элементы массива до тех пор, пока не встретится число 3. Как только встретится - цикл должен завершить свою работу.

Такое можно сделать с помощью инструкции break - если выполнение цикла дойдет до нее, цикл закончит свою работу.

Давайте решим приведенную выше задачу - оборвем цикл, как только нам встретится число 3:

Var arr = ; for (var i = 0; i < arr.length; i++) { if (arr[i] === 3) { break; //выходим из цикла } else { alert(arr[i]); } }

Инструкция continue

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

Что вам делать дальше:

Приступайте к решению задач по следующей ссылке: задачи к уроку .

Когда все решите - переходите к изучению новой темы.

Последнее обновление: 08.04.2018

Циклы позволяют в зависимости от определенных условий выполнять некоторое действие множество раз. В JavaScript имеются следующие виды циклов:

    for..in

    for..of

    while

    do..while

Цикл for

Цикл for имеет следующее формальное определение:

For ([инициализация счетчика]; [условие]; [изменение счетчика]){ // действия }

Например, используем цикл for для перебора элементов массива:

Var people = ["Tom", "Alice", "Bob", "Sam"]; for(var i = 0; i

Первая часть объявления цикла - var i = 0 - создает и инициализирует счетчик - переменную i. И перед выполнением цикла ее значение будет равно 0. По сути это то же самое, что и объявление переменной.

Вторая часть - условие, при котором будет выполняться цикл. В данном случае цикл будет выполняться, пока значение i не достигнет величины, равной длине массива people. Получить длину массива можно с помощью свойства length: people.length .

Третья часть - приращение счетчика на единицу.

И так как в массиве 4 элемента, то блок цикла сработает 4 раза, пока значение i не станет равным people.length (то есть 4). И каждый раз это значение будет увеличиваться на 1. Каждое отдельное повторение цикла называется итерацией. Таким образом, в данном случае сработают 4 итерации.

А с помощью выражения people[i] мы сможем получить элемент массива для его последующего вывода в браузере.

Необязательно увеличивать счетчик на единицу, можно производить с ним другие действия, например, уменьшать на единицу:

Var people = ["Tom", "Alice", "Bob", "Sam"]; for(var i = people.length - 1; i >= 0; i--){ console.log(people[i]); }

В данном случае массив выводится с конца, а перебор массива начинается с i = 3 до i = 0.

for..in

Цикл for..in предназначен для перебора массивов и объектов. Его формальное определение:

For (индекс in массив) { // действия }

Например, переберем элементы массива:

Var people = ["Tom", "Alice", "Bob", "Sam"]; for(var index in people){ console.log(people); }

Цикл for...of

Цикл for...of похож на цикл for...in и предназначен для перебора коллекций, например, массивов:

Let users = ["Tom", "Bob", "Sam"]; for(let val of users) console.log(val);

Текущий перебираемый элемент коллекции помещается в переменную val, значение которой затем выводится на консоль.

Цикл while

Цикл while выполняется до тех пор, пока некоторое условие истинно. Его формальное определение:

While(условие){ // действия }

Опять же выведем с помощью while элементы массива:

Var people = ["Tom", "Alice", "Bob", "Sam"]; var index = 0; while(index < people.length){ console.log(people); index++; }

Цикл while здесь будет выполняться, пока значение index не станет равным длине массива.

do..while

В цикле do сначала выполняется код цикла, а потом происходит проверка условия в инструкции while. И пока это условие истинно, цикл повторяется. Например:

Var x = 1; do{ console.log(x * x); x++; }while(x < 10)

Здесь код цикла сработает 9 раз, пока x не станет равным 10. При этом цикл do гарантирует хотя бы однократное выполнение действий, даже если условие в инструкции while не будет истинно.

Операторы continue и break

Иногда бывает необходимо выйти из цикла до его завершения. В этом случае мы можем воспользоваться оператором break :

< array.length; i++) { if (array[i] > 10) break; document.write(array[i] + "
"); }

Данный цикл перебирает все элементы массива, однако последние четыре элемента не будут выведены в браузере, поскольку поверка if (array[i] > 10) прервет выполнение цикла с помощью оператора break, когда перебор массива дойдет до элемента 12.

Если нам надо просто пропустить итерацию, но не выходить из цикла, мы можем применять оператор continue :

Var array = [ 1, 2, 3, 4, 5, 12, 17, 6, 7 ]; for (var i = 0; i < array.length; i++) { if (array[i] > 10) continue; document.write(array[i] + "
"); }

В этом случае, если программа встретит в массиве число, больше 10, то это число не будет выводиться в браузере.

|

Поскольку while и do…while основаны на условиях, они выполняются, когда заданный оператор оценивается как true. Оператор for также основан на условиях, но он предоставляет дополнительные функции, такие как счетчик цикла, позволяющий заранее установить количество итераций цикла.

Данный мануал научит вас использовать циклы for, for…of и for…in, которые являются неотъемлемыми элементами программирования в JavaScript.

Циклы for

Цикл for может использовать до трех опциональных выражений для повторного выполнения блока кода.

Рассмотрим синтаксис цикла.

for (инициализация; условие; финальное выражение) {
// код, который нужно выполнить
}

  • Инициализация (если указано) запускает счетчик и объявляет переменные.
  • Далее обрабатывается условие. Если оно истинно, программа выполнит последующий код; если оно ложно, цикл прервется.
  • Затем обрабатывается код, который нужно выполнить.
  • Если указано финальное выражение, оно обновляется, после чего цикл возвращается к обработке условия.

Чтобы понять, как это работает, рассмотрим базовый пример.


for (let i = 0; i < 4; i++) {
// Print each iteration to the console
console.log(i);
}

Если запустить этот код, вы получите такой результат:

0
1
2
3

В приведенном выше примере цикл for начинается с переменной let i = 0, которая запустит цикл со значения 0. В цикле задано условие i < 4, а это означает, что до тех пор, пока значение i меньше 4, цикл будет продолжать работу. Финальное выражение i++ определяет счетчик для каждой итерации цикла. console.log(i) выводит числа, начиная с 0, и останавливается, как только i равняется 4.

Без цикла код, выполняющий те же действия, был бы таким:

// Set initial variable to 0
let i = 0;
// Manually increment variable by 1 four times
console.log(i++);
console.log(i++);
console.log(i++);
console.log(i++);

Без цикла блок кода состоит из большего количества строк. Чтобы увеличить количество чисел, пришлось бы внести в код еще больше строк.

Давайте рассмотрим каждое выражение в цикле.

Инициализация

Первое выражение в цикле – инициализация.

Оно объявляет переменную i с помощью ключевого слова let (также можно использовать ключевое слово var) и присваивет ей значение 0. Вы можете использовать в циклах любые имена переменных, но переменная i ассоциируется со словом «итерация» (iteration) и не перегружает код.

Условие

Как и циклы while и do…while, циклы for обычно имеют условие. В данном примере это:

Это значит, что выражение оценивается как истинное, пока значение i меньше 4.

Финальное выражение

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

В данном примере цикл увеличивает переменную на единицу. Выражение i++ делает то же самое, что и i = i + 1.

В отличие от начала и условия, финальное выражение не заканчивается точкой с запятой.

Тело цикла

Теперь вы знаете все компоненты цикла for. Взглянем на код еще раз.

// Initialize a for statement with 5 iterations
for (let i = 0; i < 4; i++) {
console.log(i);
}

Первое выражение задает исходное значение переменной (0), второе определяет условие (цикл выполняется, пока i меньше 4), а третье – задает шаг каждой итерации (в данном случае значение будет увеличиваться на 1).

Консоль будет выводить значения: 0, 1, 2 и 3. Затем цикл прервется.

Опциональные выражения

Все тир выражения цикла for опциональны. К примеру, вы можете написать такой же цикл for, но пропустить инициализацию и инициализировать переменную вне цикла.


let i = 0;
// Initialize the loop
for (; i < 4; i++) {
console.log(i);
}
0
1
2
3

В данном случае первый символ точки с запятой указывает, что начало пропущено.

Примечание: Даже если вы не используете одно из выражений, необходимо ставить точку с запятой, иначе цикл будет интерпретирован неверно.

Также для примера можно убрать из цикла условие. Чтобы цикл остановился после того, как значение переменной станет больше 3, используйте if и break.

// Declare variable outside the loop
let i = 0;
// Omit initialization and condition
for (; ; i++) {
if (i > 3) {
break;
}
console.log(i);
}
0
1
2
3

Важно! В циклах без условия обязательно нужно использовать оператор break. Иначе цикл не сможет прерваться (такие циклы называются бесконечными) и станет причиной сбоя браузера.

Также из цикла можно удалить финальное выражение. Шаг можно указать в конце кода цикла. При этом нужно обязательно оставить в скобках оба символа «;», иначе цикл не будет работать.

// Declare variable outside the loop
let i = 0;
// Omit all statements
for (; ;) {
if (i > 3) {
break;
}
console.log(i);
i++;
}
0
1
2
3

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

Изменение массивов

Цикл for можно использовать для изменения массивов.

В следующем примере показано, как создать пустой массив и заполнить его переменными с помощью счетчика цикла. Создайте файл modifyArray.js и добавьте в него такой код:

// Initialize empty array
let arrayExample = ;
// Initialize loop to run 3 times
for (let i = 0; i < 3; i++) {
// Update array with variable value
arrayExample.push(i);
console.log(arrayExample);
}

Запустите программу. Она выведет:

[ 0 ]
[ 0, 1 ]
[ 0, 1, 2 ]

Заданный цикл выполняется до тех пор, пока i < 3 перестанет быть истинным. Массив arrayExample выводится в консоль в конце каждой итерации. Так вы можете увидеть, как массив пополняется новыми значениями.

Длина массива

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

// Declare array with 3 items
let fish = [ "flounder", "salmon", "pike" ];
// Initalize for loop to run for the total length of an array
for (let i = 0; i < fish.length; i++) {
// Print each item to the console
console.log(fish[i]);
}

Такая программа выдаст результат:

flounder
salmon
pike

Этот цикл перебирает каждый индекс массива с помощью fish[i]. Это приводит к динамическому обновлению индекса при каждой итерации.

Цикл for - наиболее используемый вариант организации цикла в JavaScript.

Его конструкция выглядит так:

For (начало; условие; шаг) { /* тело цикла */ }

Всё на самом деле просто. Давайте рассмотри пример:

Var i; for (i = 1; i

В этом примере:

  • Начало цикла : i = 1 (начиная со значения i = 1)
  • Условие цика : i
  • Шаг цикла : i++ (при каждом шаге цикла i увеличивать на 1)
  • Тело цикла : document.write("

    Выполняется шаг цикла номер:" + "

    "); (выводить на экран сообщение)

Пошаговый алгоритм выполнения этого цикла for, более детально:

  1. Начало цикла: переменной i присваивается значение 1. Эта часть цикла выполняется один раз.
  2. Проверяется условие цикла (i 5) - конец цикла.
  3. Выполняется тело цикла.
  4. Выполняется шаг цикла. В шашем случае i++. Он всегда выполняется после тела цикла.
  5. Возвращение на пункт 2.

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

Переменная i после завершения цикла не исчезает. Она продолжает существовать и её значение после завершения цикла будет равно 6.

Давайте обощим эти данные в новом примере:

Var i; for (i = 1; i

Тут для создания тела цикла фигурные скобки не использовались.

Фигурные скобки {...} образуют блок в JavaScript - это одна из консрукций языка. То есть, если после оператора цикла for стоят фигурные скобки, это значит что обработчик JavaScript должен выполнить весь блок JavaScript.

Аналогично блоку, в цикле for можно указать функцию. Вот пример:

For (var i = 1; i

Но вот при объявлении функции фигурные скобки {...} обязательны. Их отсутствие приведёт к ошибке.

Обратите внимание, в этом цикле переменная i объявлена в начале цикла: for (var i = 1 ; i

Пропуск частей for

Вообще, начало цикла можно и не прописывать:

Var i = 1; for (; i

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

Также можно убрать шаг:

Var i = 1; for (; i

Этот цикл for превратился в аналог цикла while (i

В условие можно поставить выражение, изменяющее переменную.

For (i = 10; i--;) { document.write("

Выполняется шаг цикла: " + i + ".

"); }

Так как интерпретатор JavaScript когда ожидает получить логическое значение, любое значение приводит к логическому типу, то когда в результате очередного дикремента переменная i станет равной 0 (ложь), цикл остановится.

Безконечный цикл for

Да, да, я знаю что правильно писать бесконечный:)

Итак, цикл будет безконечный, если условие всегда будет истиной. Вот пример:

For (var i = 1; i

В этом примере переменная i будет уменьшаться и никогда не станет больше пяти. Цикл будет выполняться вечно. Попробуйте запустить этот скрипт. У меня Хром "задумался" и ничего не выдал на экран, а продолжал думать и думать.

Будте внимательны и избегайте случайного создания безконечных циклов.

Прерывание цикла for

Для прерывания цикла for, как и для прерывания любого другого цикла, используется команда break . Когда обработчик JavaScript обнаруживает команду break в теле цикла, он останавливает выполнения цикла и начинает выполнять инструцкии сценария, следующие за циклом. если такие имеются.

В следующем примере мы остановим цикл на третьей итерации (третьем шаге).

For (var i = 1; i

Немного усложним пример

Выполним только 100 итераций безконечного цикла.

Var $counter = 1; for (var i = 1; i

Следующая итерация: continue

Команда continue завершает текущую итерацию и начинает выполнять следующую.

Директива continue «младшая сестра» директивы break, она останавливает только итерацию, а не весь цикл.

For (var i = 1; i

Цикл ниже использует continue , чтобы выводить нечетные значения:

For (var i = 0; i

Конечно, нечётные значения можно вывести при помощи такого цикла без директивы continue :

For (var i = 0; i

Директивы break / continue в операторе "?"

Давайте кратко опишем оператор вопросительный знак "? ". Он похож на конструкцию if .

Логическая конструкция:

If (условие) { a(); } else { b(); }

Работает также, как и код с оператором "? ".

Условие? a() : b(); var i = 2; document.write("

Часть 1.

"); if (i == 2) document.write("

Условие сработало.

"); else document.write("

Условие не сработало.

"); document.write("

Часть 2.

"); i == 2 ? document.write("

Условие сработало.

") : document.write("

Условие не сработало.

");

Так вот, важно , нельзя использовать break/continue справа от оператора "? "

В JavaScript синтаксические конструкции, не возвращающие значений, запрещено использовать в операторе "? ".

Нижний пример не рабочий, в нём содержится ошибка:

For (var i = 0; i

Метки для break / continue

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

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

Метки - единственный способ для команд break и continue повлиять на выполнение внешнего цикла.

Инструкция метки (англ. label) используется только вместе с break или continue для альтернативного выхода из цикла.

Метка имеет синтаксис "имя:", имя метки должно быть уникальным. Метка ставится перед циклом, в той же строке или с переносом строки.

Аналогично можно в этом месте использовать директиву break . Но в случае её использования, как вы понимаете, выполнение циклов прекратится.

Var i, j; metka1: for (i = 0; i

В языке JavaScript нет оператора goto , как в PHP, возможно использовать только метки с break или continue .

Метки редко используются при программировании на JavaScript, так как считается что они делают код сложнее в чтении ипонимании. При кодировании рекомендуется использовать функции.