03.md 18 KB

Занятие 3. Циклы и массивы.

Отладка

Браузер на обычном компьютере или ноутбуке содержит в себе средства отладки вида страницы (HTML/CSS) и сопутствующего JS-кода. Эти средства называются Developer Tools и вызываются по F12 Ctrl-Shift-I или Правая Кнопка->Посмотреть код элемента на странице.

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

Для начала пошаговой отладки устанавливается точка останова - строка кода, с которой обычный режим выполнения прерывается и начинается отладка по шагам. В Developer Tools найдите вкладку Source, в ней свой файл c кодом, и кликните на номере нужной строки слева от текста кода.

Если вы используете repl.it, другие онлайн-IDE или console, то у вас будут определенные сложности с нахождением вашего кода и строки в нём. Поэтому вы можете вставить ключевое слово debugger в ваш код - это работает так же, как точка останова на строке в Developer Tools.

Отладка по шагам.

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

  • Step over next function call (F10) - следующий шаг/оператор в вашем коде. После выполнения каждой команды вы можете ознакомится со значениями переменных, наведя на них курсор мыши, написав их в консоли, или же используя вкладку Watch
  • Resume script execution (F8) - переход из режима отладки по шагам в обычный режим выполнения кода. Таким способом вы пропускаете хорошо отлаженные части кода. Этот режим может быть прерван следующей точкой останова или ключевым словом debugger в коде.

Пошаговая отладка и консоль.

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

Отладочный вывод

Вы всегда можете добавить console.log в место, где хотите получить информацию о состоянии программы. Этот способ хорош как дополнение к остальным. Так же вы можете написать определенное условие для отладки, вставить в него console.log и поставить точку останова.

Комментирование как инструмент отладки

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


var a = b + 5;
//a += prompt(); //однострочный комментарий, временно "выключаем" ввод пользователя.
//console.log(a); //выключенный отладочный вывод, что бы не мозолил глаза, пока не нужен
var c = somePerfectFunction(a) //функция работает хорошо
/* if (blahala){
asdfasdf

explodeMoon()
*/ //многострочный комментарий с большим куском кода, выключен потому что в нём куча ошибок.

Задание 1

Поиграться с вашим ДЗ, добавив туда точки останова, и посмотреть, чему равны переменные и как ведут себя if и switch в зависимости от значения переменных. Отладьте задание о логине и пароле в ДЗ.

Циклы.

Цикл - последовательность операторов, которая повторяется.

Цикл состоит из:

  • тела цикла, т. е. блока кода, который повторяется,
  • условия, которое проверяется перед очередным повтором цикла.

Однократное выполнение тела цикла называется итерацией.

Ключевые слова break и continue.

Эти ключевые слова применяются в теле и позволяют изменять логику работы цикла:

  • break обрывает выполнение цикла в любом месте тела - программа продолжается после цикла;
  • continue обрывает выполнение текущей итерации и начинает следующую итерацию.

Цикл while

Цикл while выполняется, пока условие истинно и прекращает свое выполнение как только условие становится ложно:

var password = '';
var rightPassword = 'qwerty';

while (password != rightPassword){ //Пока пароль не равен верному...
	password = prompt('Введите пароль:', ''); //спрашивать пароль
} //возвращаемся на while (....)

Цикл while в некотором смысле схож с if - тело выполняется если условие верно; однако if выполняет тело однократно, а while выполняет тело пока условие верно.

break

var password = '';
var rightPassword = 'qwerty';

while (password != rightPassword){ //Пока пароль не равен верному...
	password = prompt('Введите пароль:', ''); //спрашивать пароль
	if (password == null){ //пользователь нажал отмену...
		break; //прерываем цикл
	}
}

continue

var user 	  = '';
var rightUser     = 'admin';
var password 	  = '';
var rightPassword = 'qwerty';

while (user != rightUser || password != rightPassword){ //Пока пользователь не равен верному или пароль не равен верному...
	user     = prompt('Введите имя пользователя:', ''); //спрашивать пользователя
	if (user != rightUser){ //нет смысла спрашивать пароль, если пользователь неверный, поэтому...
		continue;      //новая итерация.
	}
	password = prompt('Введите пароль:', ''); //спрашивать пароль
}

Цикл do ... while

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

var password = '';
var rightPassword = 'qwerty';

do{ 
	password = prompt('Введите пароль:', ''); //спрашивать пароль
}while (password != rightPassword) //Пока пароль не равен верному...

Цикл for

Это самый сложный и мощный цикл, в синтаксисе заголовка которого, кроме условия, присутствуют еще и другие операции:

  • инициализация, т. е. то, что делается перед началом цикла
  • условие продолжения (как в while)
  • операции, которые выполняются после каждой итерации

В общем виде цикл for выглядит так:

for (init1,init2,...;condition;iter1,iter2...){
....
}

Где:

  • initN - это те или иные инициализирующие операторы, которые выполняются перед началом выполнения цикла
  • condition - условие продолжения
  • iterN - операции, выполняющиеся после какждой итерации
for (var i=0;i<10;i++){
	console.log(i);
}

Этот цикл считает от 0 до 9, каждую итерацию выводя значения i в консоль. Вопрос: чему будет равен i после цикла?

var letters = "abcdefghij";
for (var i=0;i<10;i++){
	console.log(i + ":" + letters[i]);
}

Обратите внимание на работу со строкой как с массивом.

Задание

Напишите аналогичный цикл while

for (var i=10,str="";i>0;i--,str+="#"){
	console.log(i, str);
}

Вопрос: чему будет равен i и str после цикла?

Задание

Напишите аналогичный цикл while

Таким образом становится ясно, что for - не более чем сокращенная форма while, или, другими словами, синтаксический сахар.

Вложенные циклы

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

Например:

var i = 'a';
var str = "";
for (j=0;j<10;j++){
	str += i;
}
console.log(str);

Таким образом мы можем создать строку любой длины с любым символом. А теперь оборачиваем этот цикл внешним:

for (var i=0;i<10;i++){
	var str = "";
	for (j=0;j<10;j++){
		str += i;
	}
	console.log(str);
}

Задание

Напишите вложенный цикл, который будет создавать HTML для таблицы умножения. Используйте тэги table, tr, td. Таблица должна создаваться в строковой переменной. Для вывода этой переменной используйте document.write.

Пример:

0 1 2
1 1 2
2 2 4

Таблица выше создается с помощью следующего синтаксиса HTML. Для проверки вы просто можете скопировать этот HTML в какой-то файл .html и пробовать открыть его в браузере. Или же присвоить его какой-то строковой переменной (str) и попробовать вывести в окно браузера: document.write(str). Так же можно попробовать его в repl.it или jsfiddle.

<table>
    <tr>
        <td>0</td>
        <td>1</td>
        <td>2</td>
    </tr>
    <tr>
        <td>1</td>
        <td>1</td>
        <td>2</td>
    </tr>
    <tr>
        <td>2</td>
        <td>2</td>
        <td>4</td>
    </tr>
</table>

То есть, таблица начинается с тэга <table> и заканчивается парным тэгом </table>. Каждая строка начинается с тэга <tr> и заканчивается парным закрывающим тэгом </tr>. Каждая ячейка в строке начинается тэгом <td> и заканчивается парным закрывающим тэгом </td>. Между парой тэгов <td> и </td> находится текст, который отображается в ячейке. Используя вложенные циклы и строковую конкатенацию (str += "то, что надо добавить к строке") сделайте код, который генерирует таблицу умножения. Для вывода таблицы используйте document.write(str).

Массивы

Массив - это упорядоченная структура данных, состоящая из нумерованных ячеек. Нумерация идет от 0. Например строка (String) - массив символов. Доступ к ячейкам массива происходит по её номеру в квадратных скобках:

var myName = "asmer"
myName[0]
myName[4]
myName.length

Для оперирования с массивами зачастую используются циклы.

Задание

Выведите какую-либо строку побуквенно, используя цикл for. Длина массива или строки находится в свойстве length.

Определение массива

Массивы можно определять несколькими способами

var oopWay = new Array(1,2,3);
var inlineWay = ["some", "other", "array'"];

Операции с элементами массива

В общем те же самые, что и с обычными переменными. Просто не забудьте добавить квадратные скобки и индекс (номер элемента):

var oopWay = new Array(1,2,3);
var inlineWay = ["some", "other", "array'"];

oopWay[0]++
oopWay[2] = oopWay[0] - oopWay[1]
inlineWay[3] = inlineWay[0] + inlineWay[1] + inlineWay[2]

Для обхода всего массива используйте цикл for:

for (var i=0;i<arr.length;i++){
    //какие-то операции с arr[i]
    console.log(arr[i])
}

Задание

Перепишите

inlineWay[3] = inlineWay[0] + inlineWay[1] + inlineWay[2]

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

Присвоение, сравнение, копирование массивов и различия со строками.

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

a = b = [1,2,3]
//[1, 2, 3]
a == b
//true
a[0] = 500
//500
a
//[500, 2, 3]
b
//[500, 2, 3]

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

a = b.slice()
//[500, 2, 3]
a
//[500, 2, 3]
a[0] = 1
//1
b
//[500, 2, 3]
a
//[1, 2, 3]
a == b
//false

Однако копия не равна оригиналу несмотря на одинаковое содержимое:

a
//[1, 2, 3]
b
//[500, 2, 3]
b[0] = 1
//1
b
//[1, 2, 3]
a == b
//false

Пустой массив равен false:

[] == false
//true
!![]
//true эта проверка не работает для объектов, кроме null
false == []
//true
[] == []
//false ведь это два разных массива

Практикум по методам класса Array