« Полезняшки. PHPShell - скрипт php, позволяющий выполнять на веб-сервере shell-команды | Работа с базой данных mysql из c++ » |
Мелкие хитрости в отладке кода с помощью alert. Часть 1
Я полагаю - не секрет, что каждый программист тратит значительную долю своего времени не на написание кода, а на отладку, попытку найти ошибки, и понять “почему же эта штука работает не так, как я планировал”. Следовательно, нам просто необходимы качественные и удобные средства для отладки, профилирования кода – все то, что позволяет понять нам “что же там происходит на самом деле внутри?”. Рядом находятся инструменты, служащие для управления базой багов, ведения журналов пожеланий, исправлений и заметок к очередному билду - средства позволяющие организовать прочную связь “постановщик задач - программист - тестировщик”. Сегодня я расскажу об отладке javascript-кода.Самым первым средством для отладки, с ним знаком каждый начинающий свой путь в веб-разработке, - это alert. Функция alert получает в качестве параметра некоторую строку текста и выводит ее на экран с помощью диалогового окна сообщения (см. рис. 1).
<script>
var x = 10;
var y = x / 3;
alert (y); // чему же равна переменная y?
</script>
Чем плох такой подход? Да почти, всем. Во-первых, с помощью alert вы может выводить на экран только простые типы данных: числа, строки. Если же вас интересует внутреннее устройство некоторого объекта, то приходится писать код, который перебирает все свойства объекта и выводит их по очереди. И хорошо, если это простенький объект, например, такой:
var obj = {fio: "Bill", age: 21, sex: "male"};
alert (obj); // а как насчет вывода содержимого сложного объекта?
Давайте напишем функцию которая получает на вход объект, перебирает в цикле его свойства и формирует строку для последующего вывода ее alert-ом. В основе этой функции лежит цикл “for (i in obj)” – его назначение перебрать все свойства некоторого объекта, имена этих свойств будут помещаться в переменную I, затем же используя обращение obj[i] мы узнаем и само значение свойства. Чтобы меня не сразу “запинали” за приведенный код, я решил добавить проверку на рекурсию, так если объект содержит внутри себя ссылку прямым или косвенным образом на самого себя, то очевидно, что необходимо такую ситуацию обработать как-то по-особенному – если мы не остановимся и будем всякий встретившийся объект печатать, то попадем в бесконечный, не цикл, но бесконечную рекурсию вызовов функции _objDump. Для этого я ввел второй параметр для _objDump – это массив пройденных уже элементов, при первом вызове из функции objDump я кладу в этот массив ссылку на собственно сам распечатываемый элемент. Третий параметр – level – нужен для “красоты”. Перед выводом имен полей и значений объектов я печатаю некоторое количество пробелов, так чтобы в случае сложной иерархии вложенностей объектов друг в друга вывод был бы в виде “лесенки” (см. рис. 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)); // а как насчет вывода содержимого сложного объекта?
var obj = {fio: "Bill", age: 21, sex: "male", complex : {classrom: 314, teacher: "Vasyano"} };
obj.gravity = 1.0;
obj.self = obj;// а вот теперь хитрость, наш объект ссылается на самого себя -
// вполне себе привычная ситуация для сложных структур данных
alert (obj.toSource ());
Маленький трюк: если содержимое объекта очень, очень, и еще раз, очень большое, то вывод с помощью alert-а приведет к тому, что окно сообщения станет огромным, обрежется по нижнему краю, например, так (см. рис. 5). Если же открыть страницу в opera, то окно сообщения будет позволять выделять текст сообщения, следовательно, вы его копируете в буфер обмена, открываете блокнот, вставляете и пытаетесь понять, что означает этот десяток килобайт “букав” (см. рис. 6).
Какие еще “фишки” можно добавить для 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-а
В следующий раз я расскажу о более “продвинутых” средствах отладки, специфических для конкретных браузеров.
« Полезняшки. PHPShell - скрипт php, позволяющий выполнять на веб-сервере shell-команды | Работа с базой данных mysql из c++ » |