Мелкие хитрости в отладке кода с помощью alert. Часть 1

Источник: http://black-zorro.jino-net.ru/

Я полагаю - не секрет, что каждый программист тратит значительную долю своего времени не на написание кода, а на отладку, попытку найти ошибки, и понять “почему же эта штука работает не так, как я планировал”. Следовательно, нам просто необходимы качественные и удобные средства для отладки, профилирования кода – все то, что позволяет понять нам “что же там происходит на самом деле внутри?”. Рядом находятся инструменты, служащие для управления базой багов, ведения журналов пожеланий, исправлений и заметок к очередному билду - средства позволяющие организовать прочную связь “постановщик задач - программист - тестировщик”. Сегодня я расскажу об отладке javascript-кода.

Самым первым средством для отладки, с ним знаком каждый начинающий свой путь в веб-разработке, - это alert. Функция alert получает в качестве параметра некоторую строку текста и выводит ее на экран с помощью диалогового окна сообщения (см. рис. 1).

<script>
var x = 10;
var y = x / 3;
alert (y); // чему же равна переменная y?
</script>

debug_javascript_1 

Чем плох такой подход? Да почти, всем. Во-первых, с помощью alert вы может выводить на экран только простые типы данных: числа, строки. Если же вас интересует внутреннее устройство некоторого объекта, то приходится писать код, который перебирает все свойства объекта и выводит их по очереди. И хорошо, если это простенький объект, например, такой:


var obj = {fio: "Bill", age: 21, sex: "male"};
alert (obj); // а как насчет вывода содержимого сложного объекта?
Функция alert сообщит нам, что переменная obj представляет собой объект, но ни слова о том какие поля (fio, age, sex) и чему они равны (см. рис. 2).

 


debug_javascript_2

Давайте напишем функцию которая получает на вход объект, перебирает в цикле его свойства и формирует строку для последующего вывода ее alert-ом. В основе этой функции лежит цикл “for (i in obj)” – его назначение перебрать все свойства некоторого объекта, имена этих свойств будут помещаться в переменную I, затем же используя обращение obj[i] мы узнаем и само значение свойства. Чтобы меня не сразу “запинали” за приведенный код, я решил добавить проверку на рекурсию, так если объект содержит внутри себя ссылку прямым или косвенным образом на самого себя, то очевидно, что необходимо такую ситуацию обработать как-то по-особенному – если мы не остановимся и будем всякий встретившийся объект печатать, то попадем в бесконечный, не цикл, но бесконечную рекурсию вызовов функции _objDump. Для этого я ввел второй параметр для _objDump – это массив пройденных уже элементов, при первом вызове из функции objDump я кладу в этот массив ссылку на собственно сам распечатываемый элемент. Третий параметр – level – нужен для “красоты”. Перед выводом имен полей и значений объектов я печатаю некоторое количество пробелов, так чтобы в случае сложной иерархии вложенностей объектов друг в друга вывод был бы в виде “лесенки” (см. рис. 3).

 

debug_javascript_3 

// назначение этой функции просто вызвать функцию _objDump,
// которая собственно и содержит цикл перебирающий свойства объекта obj
function objDump (obj, except) {
return _objDump (obj, [obj], 0);
}

function inArray (arr, elt){
for (var i = 0; i < arr.length; i++)
if (elt == arr[i]) return i;
return false;
}
function _objDump (obj, used, level) {
var rez = '';
var spaces = "";
for (var i = 0; i < level; i++)
spaces += " ";
rez += spaces+('---Dumping object: ' + obj.toString () + "n");
for (var i in obj) {
if (used && inArray(used ,obj[i]) !== false) {
rez += spaces+"->"+"!Self Reference"+"n";
continue;
}
rez += (spaces+"->" + i + ': ');
if ( (typeof obj[i]) == "object"){
try{
rez += _objDump (obj[i], used.slice(0).push(obj[i]), 1 +level) +"n";
}catch (eee) {rez += spaces+"->"+"!Error"+"n";}
}
else
rez +=(spaces+"->"+obj[i] + "n");
}
return rez;
}

// ------------ а теперь пример использования функций --------
var obj = {fio: "Bill", age: 21, sex: "male", complex : {classrom: 314, teacher: "Vasyano"} };
obj.gravity = 1.0;
obj.self = obj;// а вот теперь хитрость, наш объект ссылается на самого себя -
// вполне себе привычная ситуация для сложных структур данных
alert (objDump(obj)); // а как насчет вывода содержимого сложного объекта?
По правде говоря, достичь той же самой цели – вывода содержимого объекта в удобочитаемом виде - можно и меньшими затратами. В javascript для массивов существует метод toSource, который формирует строку в формате “похожем на json” и, что особенно приятно, корректно обрабатывает сложные иерархии объектов и рекурсию.


var obj = {fio: "Bill", age: 21, sex: "male", complex : {classrom: 314, teacher: "Vasyano"} };
obj.gravity = 1.0;
obj.self = obj;// а вот теперь хитрость, наш объект ссылается на самого себя -
// вполне себе привычная ситуация для сложных структур данных
alert (obj.toSource ());
Заметьте на рис. 4, что значением свойства self является #1, и это же имя встречается в самом начале выводимой строки. Хотя и первый вариант кода имеет право на существование.

 

debug_javascript_5 

debug_javascript_6_400 

Какие еще “фишки” можно добавить для alert? Если код достаточно велик и alert-ы срабатывают многократно, то неплохо было бы создать какой-то журнал, в котором всегда можно было бы глянуть чему равна была та или иная переменная. Плюс работа с всплывающим окном крайне не удобна при отладке скриптов работающих с обработкой событий мыши. Например, делая классическое выпадающее меню вы столкнетесь с тем, что появление диалогового окна alert-а приводит к генерации лишних событий “onmouseout” и “onmouseover”. Лучше всего, чтобы при запуске скрипта было создано всплывающее окно, в котором бы и накапливался весь текст выводимый alert-ами, например, так:

var extw = null;// ссылка на окно для вывода значений переменных
// функция записывает в окно, дату и значение переменной – переданной как параметр
function alert2 (what){
if (extw == null)
extw = window.open ();
extw.document.write ('<b>'+new Date()+'</b> => <pre>'+what+'</pre><br />');
}

var obj = {fio: "Bill", age: 21, sex: "male", complex : {classrom: 314, teacher: "Vasyano"} };
obj.gravity = 1.0;
obj.self = obj;// а вот теперь хитрость, наш объект ссылается на самого себя
// - вполне себе привычная ситуация для сложных структур данных

alert2 (objDump(obj));// используем новую и улучшенную версию alert-а
alert ('старый добрый alert'); // используем классический alert
alert2 ('и еще немножко данных'); // используем новую и улучшенную версию alert-а
Результат работы скрипта показан на рис. 7.

 

debug_javascript_7_400 

В следующий раз я расскажу о более "продвинутых" средствах отладки, специфических для конкретных браузеров.



Опубликовал admin
3 Ноя, Суббота 2007г.



Программирование для чайников.