Глаза страшат, а руки делают

ЧАСТЬ ПЕРВАЯ


6. Операторы цикла: for, for...in

  • арифметическая прогрессия (сумма);
  • арифметическая прогрессия (числа по порядку);
  • таблица умножения;
  • for...in: извлечение свойств из объекта

Хотя JavaScript не универсален и не всемогущ, не велик и не ужасен, это всё же язык программирования, и он позволяет выполнять сложные математические вычисления. Иногда возникают ситуации, когда требуется многократно выполнить программный код, пока не выполнится определённое условие. Допустим, сдать карты в пасьянсе (очень хитроумная задача). Или сложить подряд числа от 1 до 15 (простейший вариант). Для этих целей существуют операторы, которые позволяют выполнять циклы — повторяющиеся действия.

Оператор for

Его синтаксис напоминает оператор if: условие в круглых скобках, код — в фигурных. Но условие более сложное:

for (начало; область действия; шаг)
{код}

Начало: здесь задаётся начальное условие, при котором оператор включается.

Область действия: границы, в которых выполняется код.

Шаг: способ продвижения цикла от начального к конечному условию.

Арифметическая прогрессия

Предположим, нам надо узнать и записать на странице сумму всех чисел подряд от 1 до 10.

var i, x = 0;
for (i = 1; i <= 10; i++)
{x += i}
document.write(x)

Результат:

Разберём:

Объявляем переменную счётчика i и переменную, собирающую знчения — x. Назначим ей нулевое значение. Все значения должны куда-то складываться, а неназначенная переменная — это Null («посмотришь на его место, и нет его»).

Начальное значение счётчика — 1. Граница — до 10 включительно, то есть меньше или равно 10. Шаг — 1 (каждое следующее целое число).

Привыкайте к специальным обозначениям. Если не помните, что такое ++ или +=, зайдите в прошлый урок и посмотрите табличку.

В коде x += i каждое новое значение счётчика приплюсовывается к переменной x.

Затем выходим из цикла и публикуем на странице итоговое значение.

А теперь чуть поинтереснее.

Вытащим на страницу все промежуточные значения счётчика. Для этого поместим document.write не выходя из цикла (то есть внутри фигурных скобок). А чтобы числа не печатались сплошной нераздельной строкой, добавим в него разделитель:

var i, x = 0;
for (i = 1; i <= 10; i++)
{x += i
document.write(x + "; ")
}

Результат:

А как сделать, чтобы вся строка заканчивалась, скажем, точкой?

Для этого скомбинируем этот оператор с уже известным нам оператором if...else. То есть при последнем значении i (10) у нас должна выпасть точка, при остальных — точка с запятой и пробел:

var i, x = 0;
for (i = 1; i <= 10; i++)
{x += i
    if (i == 10) {document.write(x + ".")}
    else {document.write(x + "; ")}
}

Результат:

Примечание

Кстати, вот оно, реальное различие методов write и writeln. Вместо пробела здесь можно применить метод writeln. Если мы запишем

 ........
 ........
    if (i == 10) {document.write(x + ".")}
    else {document.writeln(x + ";")}
 ........

то пробел делать не обязательно, его поставит сам метод.

Поскольку конечное значение счётчика — уже существующее условие, а не назначаемая величина, ставим сдвоенное равенство. И не запутайтесь в фигурных скобках.

Таблица умножения

А сейчас, допустим, нам захотелось вывести таблицу умножения — вот такую:

Да чтобы руками эту таблицу не форматировать и опечаток не наделать.

Давайте найдём алгоритм для составления такой таблицы.

Как мы помним из HTML, таблицы надстраиваются горизонтальными рядами <tr>, которые, в свою очередь, делятся на ячейки <td>.

Рассмотрим содержимое первого горизонтального ряда.

Первый множитель увеличивается на единицу. Второй остаётся без изменений.

А в вертикальных столбцах — наоборот. Первый стоит на месте, а второй приращивается.

Рядов <tr> в нашей таблице девять (от 2 до 10), а колонок <td> в каждом из них по 8 (от 2 до 9).

Попробуем сначала сформировать все <tr> через счётчик i.

var i;
for (i = 2; i <= 10; i++)
{document.write("<tr>");
document.write("</tr>")}

Почему от 2 до 10, а не от 1 до 9?

Догадайтесь с трёх раз...

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

Наш первый <tr> должен выглядеть вот так (&times; — это спецсимвол HTML для отображения «школьного» знака умножения):

<tr>
    <td>2&times;2=4</td>
    <td>3&times;2=6</td>
    <td>4&times;2=8</td>
    <td>5&times;2=10</td>
    <td>6&times;2=12</td>
    <td>7&times;2=14</td>
    <td>8&times;2=16</td>
    <td>9&times;2=18</td>
</tr>

То есть внутри него ещё один цикл — из </td>.

Эти циклы можно вкладывать, как и операции с if...else. Вложим?

Для второго цикла определим счётчик j.

var i, j;
// Сначала нарисуем саму таблицу.
// Внутренние кавычки (внутри тэгов) можно сделать одиночными,
// а можно, как здесь, воспользоваться спецсимволом (см. урок 2)
document.write("<table border=\"1\" cellspacing=\"0\" cellpadding=\"2\"
align=\"center\">")
for (i = 2; i <= 10; i++)
{document.write("<tr>");
    for (j = 2; j < 10; j++)
    {document.write("<td>" + "</td>")}
document.write("</tr>")
}
document.write("</table>")

Обратили внимание? Если i <= 10, то j < 10 (без равенства), так как горизонтальных ячеек на одну меньше.

Пустая таблица свёрстана, осталось заполнить её содержимым. Первый множитель приращивается в каждом </td>, значит, он соответствует значению j. А второй, возрастающий в каждом </tr>, — значению i:

j + "&times;" + i + "=" + (i*j)

Подставим это в наш код:

var i, j;
document.write("<table border=\"1\" cellspacing=\"0\" cellpadding=\"2\"
align=\"center\">")
for (i = 2; i <= 10; i++)
{document.write("<tr>");
    for (j = 2; j < 10; j++)
    {document.write("<td>" + j + "&times;" + i + "=" + (i * j) + "</td>")}
document.write("</tr>")
}
document.write("</table>")

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

Оператор for...in

Оператор for...in используется для анализа всех свойств объекта. В результате выполнения производится перебор свойств объекта. Переменная цикла при каждом повторении содержит значение очередного свойства. Количество повторений тела цикла равно числу свойств, определенных для объекта.

Синтаксис

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

переменная — переменная счётчика, как и для цикла for.

объект — любой объект JavaScript, который мы хотим «выпотрошить».

инструкции — код для тех действий, которые мы хотим произвести с объектом.

Итерация элементов массива

Что такое массивы, мы лучше узнаем через несколько уроков, а еще подробнее — во второй части наших занятий. Если объяснять «на пальцах», то это собрание в одном «флаконе» (объекте) маленьких «объектиков», которые мы с помощью различных кодов можем доставать из объекта-контейнера, как фокусник из цилиндра. Массивами являются некоторые встроенные коллекции документа: например, все картинки на странице — document.images, или же все ссылки — document.links.

Каждая ссылка — это отдельное свойство «большого объекта» — коллекции links. Размер всей коллекции (общее количество ссылок) тоже является её свойством. Давайте с помощью for...in пересчитаем все ссылки на данной странице.

Вообще, так сказать, «потенциальных» свойств в каждом объекте заложено очень много. И в разных браузерах эти дополнительные наборы могут отличаться. Как правило, эти «второразрядные» свойства объект не использует, и их значения являются пустой строкой или величиной null (то есть «ничто»). Но наш цикл будет добросовестно нанизывать эти километры ненужных свойств. Чтобы этого не было, мы поставим в нашей функции маленькую «заглушку» и выведем только реально существующие свойства.

function showLinks() {
 var objName = "Links"
 var obj = document.links
 var i
 var msg = ""
 for (i in obj) {
 // Заглушка: выводим свойство только когда оно не пустое
 // и не null.
  if (obj[i] != "" && obj[i] != null)
  msg += objName + "[" + i + "] = " + obj[i] + "<br>"
  }
 document.write(msg)
}

Переменная msg «нанизывает» на себя все значения свойств (если они есть) с переводом каретки (<br>), чтобы они располагались одно под другим. Чтобы вывести их на страницу, нужно теперь вызвать функцию:

<script type="text/javascript">
showLinks()
</script>

Результат

Так вот, оказывается, сколько тут ссылок...

Примечание

Это не все ссылки на странице. Последняя, 84-я находится несколькими абзацами выше этого списка.

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

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

Браузеры Mozilla и Firefox выдают ещё имена функций, которые использовались в скрипте для всплывающих меню, тем самым участвуя в образовании ссылок:

Links[item] = function item() { [native code] }
Links[namedItem] = function namedItem() { [native code] }

Полезное применение

Например, мы делаем страницу с множеством ссылок на другие страницы. Но эти другие страницы ещё не готовы. Допустим, чтобы не было неприятных «битых ссылок», мы поставили на несуществующие страницы <a href="#">текст ссылки</a>. Но если такая ссылка находится в самом низу длинной страницы, то при нажатии на неё документ будет перемещаться в начальную позицию, что тоже раздражает.

Примечание

Вообще-то можно этого избежать, поставив не <a href="#">, а <a href="#null">.

Тогда давайте в самом конце кода страницы, перед тэгом </body>, поставим скрипт, проверяющий атрибуты href всех наших ссылок:

<script type="text/javascript"> var i
for (i in document.links)
{if (document.links[i].href == self.location.href + "#")
document.links[i].href = "javascript: alert('Извините, этот материал ещё в разработке.');"}
</script>

Небольшие пояснения:

1. Когда в атрибуте ссылки href мы прописываем "#", браузер понимает это как адрес текущей страницы с нашим значком в конце. В JavaScript адрес текущей страницы находится в конструкции self.location.href. Для поиска «забитой» ссылки необходимо включить в атрибут эту конструкцию.

Примечание

Конечно, было бы проще сразу поставить все необходимые ссылки, а потом проверить присутствие/отсутствие этих страниц на сервере. Но для этого нужно воспользоваться другими языками: JavaScript — клиентский язык.

2. Почему в самом конце? Это как раз тот случай, о котором я упоминал в уроке 1. Браузер читает код страницы последовательно. Если его просят пересчитать ссылки, когда он ещё не прочитал их в документе (например, если он помещён в head), то он встанет в тупик и выдаст undefined. Если его поставить в середине страницы, то он остановится там, где находится. Поэтому нужно попросить его сделать выводы, когда он уже «переварил» всю необходимую информацию.

Просмотр свойств отдельного объекта

А теперь возьмём одну из этих ссылочек (допустим, одиннадцатую) и посмотрим, из каких свойств она состоит.

function showLinkProps() {
 var objName = "Links[11]"
 var obj = document.links[11]
 var i
 var msg = ""
 for (i in obj) {
  if (obj[i] != "" && obj[i] != null)
  msg += objName + "." + i + " = " + obj[i] + "<br>"
  }
 document.write(msg)
}

Вызываем.

<script type="text/javascript">
showLinkProps()
</script>

Смотрим.

В результате мы получили полное «досье» на эту ссылку (в «Мозилле» опять же с кучей дополнительных параметров, но без некоторых свойств, которые поддерживает только IE: innerText, outerText, outerHTML).

Сейчас нам совершенно не нужны эти подробности. Просто я показал, как работает оператор for...in. Когда вы станете более опытными, то иногда понадобится вот так вот «взламывать» объекты в поисках каких-нибудь «экзотических» свойств, с которыми можно поэкспериментировать и, возможно, решить какую-нибудь нестандартную проблему.


Итак, мы узнали:

как работает оператор цикла for;

как заглянуть внутрь объекта с помощью цикла for...in.

А также научились:

выводить сообщения на помеченные ссылки.


К следующему уроку >>


 017494