Поглиблений дебаг в JavaScript
Мітки: javascript, налагодження(дебаг)
Продвинутая отладка в JavaScript
Advanced Debugging with JavaScript
| ← Паттерн кешування для моделей | Zend Framework і переклад → |
При правильному використанні, дебагери JavaScript допомагають знайти і усунути помилки у вашому коді. Щоб ефективно користуватися дебагером, вам необхідно знати про доступні інструменти, про звичайний робочий процес налагодження в JavaScript і вимоги до коду для його ефективної налагодження. У цій статті ми обговоримо прийоми поглибленого налагодження для знаходження та виправлення помилок, використовуючи типовий веб-додаток.
Про доступність
Ця стаття розповідає про сильні сторони і розходження між інструментами налагодження і показує, як ми можемо виконувати сучасні завдання налагодження в JavaScript. Наша методика включає в себе часте застосування миші; якщо ви віддаєте перевагу використовувати клавіатуру, або ж покладайтеся на допоміжні інструменти, як зчитувачів екранів, для роботи з цими інструментами вам варто звернутися до відповідної документації, щоб дізнатися як (або якщо) ці інструменти будуть працювати.
Дебагери
Зі зростаючим вибором хороших доступних дебагерів, Javascript-програмісти можуть досягти більшого, якщо вони навчаться ними правильно користуватися. Користувацький інтерфейс дебагеру Javascript (UIs) стає краще, серед продуктів з'явилося більше стандартів, їм стало легше користуватися і таким чином, стає простіше, як експертам, так і новачкам у вивченні процесу налагодження в JavaScript.
На даний момент, інструменти налагодження існують для всіх основних веб-браузерів:
• Для Firefox існує добре відоме розширення Firebug.
• IE8 (в бета релізі, на момент написання цієї статті) виходить з вбудованим Developer Tools.
• Opera (версія 9.5 і вище) підтримує дебагер Opera Dragonfly
• У Safari є і дебагер Drosera JS і DOM viewer, що називається WebInspector. У більш пізніх версіях Safari, дебагер вбудований в WebInspector.
На даний момент, Firebug і Dragonfly є найбільш стабільними варіантами вибору. Інструменти бета версії IE8 іноді ігнорують точки зупинки (breakpoints), і на час написання цієї статті, схоже, що у WebInspector є проблеми з сумісністю Webkit.
Ознайомтеся з різними інструментами налагодження - ви ніколи не знаєте, в якому браузері з'явиться наступний баг. Так як дебагери мало чим відрізняються між собою у функціональності, легко перемикатися між ними, якщо ви вивчили хоча б один з них.
Процес налагодження
Коли ви розбираєтесь з певною проблемою, ви зазвичай слідуєте таким крокам:
1. Знаходите відповідний код, панелі перегляду коду дебагеру.
2. Встановлюєте точки зупинки (breakpoints) де може виникнути помилка.
3. Якщо це вбудований скрипт, перезапускаєте його, оновленням сторінки в браузері.
4. Чекаєте доки дебагер не призупинить виконання і не дасть можливість переглянути код крок за кроком.
5. Переглядаєте значення змінних. Наприклад, шукаєте ті змінні, які не визначені, але повинні містити значення, або повертають "false", у той час як очікується "true".
6. При необхідності, використовуєте командний рядок для виконання коду або зміни значень змінних для тестування.
7. Знаходите проблему, виділивши ту частину коду або ввід, які викликають помилку.
Щоб створити точку зупинки ви також можете додати код дебагеру до вашого коду:
Прибрати підсвітку коду
Вимоги дебагеру
Більшість дебагерів вимагають добре відформатований код. Скрипти, написані в одну лінію, ускладнюють процес пошуку помилок у по-рядкових дебагерах. Заплутаний код складно налагоджувати, особливо 'запакований', що вимагає розпаковування через eval(). Безліч бібліотек Javascript дозволяють вам вибирати між упакованим/заплутаним і добре відформатованим варіантами, даючи можливість використовувати відформатований код для налагодження.
Демонстрація налагодження
Давайте почнемо з невеликого, повного багів прикладу, для того щоб навчитися як знаходити і виправляти кожну неполадку по черзі. Прикладом є екран входу в веб-додатку.
Уявіть, що ви працюєте з цим шикарним новим веб-додатком, і ваші тестери просять вас розглянути такий список багов:
1. Повідомлення "Loading ..." в статус барі не зникає після повного завантаження програми.
2. Мова за замовчуванням є норвезька, навіть в англійських версіях Firefox і IE.
3. Десь виникає глобальна змінна prop.
Запуск дебагерів
• В Firefox вам необхідно впевнитися, що у вас встановлено розширення Firebug. Для запуску виберіть Select "Tools> Firebug> Open Firebug".
• У Opera 9.5 beta 2 і пізніше, виберіть "Tools> Advanced> Developer Tools."
• У IE8 beta, виберіть "Tools> Developer Tools."
• У Safari або WebKit, спочатку виберіть налагодження, а потім виберіть "Debug> Show Web Inspector."
Настав час запустити дебагери. Щоб слідувати демонстрації, приділіть увагу до поетапної інструкції, представленій у цій статті. Так як деякі інструкції включають в себе зміну коду, можливо, ви захочете зберегти сторінку локально і завантажити її з вашої файлової системи до початку роботи.
Баг перший: повідомлення "Loading ..." в статус барі.
Якщо ви подивитеся на додаток в Dragonfly і Firebug, ви отримаєте такий початковий вигляд, як на малюнку 1.


Малюнок 1: початковий вигляд нашої програми JavaScript у Dragonfly та Firebug відповідно.
Коли ви подивитеся на вихідний код у дебагері, зауважте, що там є функція clearLoadingMessage(), оголошена на початку коду. Це місце здається підходящим для точки зупинки (breakpoint). І ось як ми це робимо:
1. Клацніть на номер рядка в лівому полі для встановлення breakpoint в першому рядку всередині функції clearLoadingMessage().
2. Перезавантажте сторінку.
Примітка: точка зупинки повинна бути встановлена на рядок коду, який буде запускати функцію. Рядок, що містить function clearLoadingMessage () (є всього лише оголошенням функції. Установка breakpoint в цьому місці ніколи не викличе зупинку дебагеру. Замість цього встановіть точку зупинки на перший рядок всередині функції. Коли ви перезавантажите сторінку, запуск скрипта буде зупинено на breakpoint і ви побачите вивід, приблизно як на малюнку 2. (Dragonfly показаний зверху, Firebug внизу)


Малюнок 2: дебагер зупинився на breakpoint всередині clearLoadingMessage.
Давайте крок за кроком розберемо функцію. Ви бачите, що вона змінила два DOM елементи, і в рядку 28 згадується слово statusbar, що виглядає як знак. Щось на зразок такого getElements ( 'p', ( 'class':' statusbar ')) [0] знайде елемент statusbar в DOM. Чи є спосіб швидко перевірити цю теорію?
Вставте відповідний блок у командний рядок для того, щоб перевірити вашу теорію. На малюнку 3 показано три знімка екранів (Dragonfly, Firebug і IE8) після зчитування innerHTML або outerHTML елемента, повертається командою про яку сказано вище.
Для тестування цього припущення:
1. Знайдіть командний рядок:
В Firebug, перейдіть на вкладку "Console".
В Dragonfly, подивіться під панеллю вихідного коду JavaScript.
В IE8 Developer Tools, праворуч знайдіть вкладку "Console."
2. Вставте getElements ( 'p', ( 'class':' statusbar ')) [0]. InnerHTML в командний рядок.
3. Натисніть ввід.



Малюнок 3: вивід показаний у Dragonfly, Firebug і IE8, відповідно.
Командний рядок є зручним інструментом, що дозволяє швидко тестувати невеликі блоки коду. Вбудована консоль Firebug дуже зручна - якщо вивід команди є об'єктом, ви отримуєте зрозумілий вид. Наприклад, якщо це об'єкт DOM, ви отримуєте відповідну розмітку.
Ви можете використовувати командний рядок для глибокого розгляду проблеми. Розглянутий рядок JavaScript робить наступні три речі:
1. Отримує посилання на елемент statusbar. У DOM inspector view ви побачите, що відповідна розмітка це <p class='statusbar'>
2. Показує, що це firstChild, іншими словами, перший елемент всередині елемента параграф.
3. Встановлює властивість innerText.
Давайте спробуємо запустити трохи більше, ніж цю команду, з командного рядка. (Підказка: використовуйте кнопку 'стрілка вверх', для навігації назад до попередніх команд, які набиралися в полі командного рядка). Наприклад, ви можете дізнатися, яке поточне значення властивості innerText елемента, до того як воно встановлюється. Щоб перевірити це, ви можете набрати наступну команду:
Прибрати підсвітку коду
На диво, виведе ... нічого. Отже, вислів getElements( 'p', {'class':'statusbar'} )[0].firstChild відноситься до чогось в DOM, що не містить тексту або не має властивості innerText.
Отже, наступне запитання: що саме є першою нодою в елементі параграфа? Давайте поставимо це питання до командного рядка. (Результат дивіться на малюнку 4)

Малюнок 4: Командний рядок дебагера Dragonfly, виводить [object Text].
Вивід [object Text] у Dragonfly показує, що цей текстова нода DOM. Firebug показує вміст текстової ноди як посилання на оглядач DOM. Тепер ви знайшли проблему, яка пояснює перший баг: текстові ноди не мають властивості innerText - вони є тільки у Нод елементів. Отже, установка p.firstChild.innerText нічого не робить. Цей баг може бути усунутий заміною innerText на nodeValue, що є властивістю, яке визначено стандартом W3C DOM для Нод тексту.
Після чого у вас є можливість переглянути цей приклад:
1. Натисніть [F5] або кнопку запуску, для завершення скрипта, коли проблема вже була виявлена.
2. Не забудьте прибрати всі старі точки зупинки, повторним натисканням на номери рядків.
Баг другий: виявлення проблемної мови
Ви могли помітити рядок var lang; / * language * / вгорі JavaScript - код, що встановлює цю змінну, мабуть, пов'язаний, коли виконання відбувається невірно. Ви можете швидко знаходити певні речі в коді, використовуючи ручні функції пошуку, представлені обома дебагерами. У Dragonfly це знаходиться відразу над переглядом коду; в Firebug це в правому верхньому кутку користувацького інтерфейсу (дивіться малюнок 5).
Для знаходження місць, які мають відношення до локалізації:
1. Наберіть 'lang =' у рядку пошуку.
2. Встановіть точку зупинки у рядку, де призначається значення для змінної lang.
3. Перезавантажте сторінку.
WebInspector Safari також має дуже великі можливості пошуку. WebInspector дозволяє шукати по всьому коду, включаючи розмітку, CSS і JavaScript. Результати відображаються в окремій панелі, де ми можете перейти до вихідного коду за допомогою подвійного кліка, як показано на скріншоті.


Малюнок 5: пошук за допомогою дебагерів Dragonfly і WebInspector.
Для відстеження, що робить ця функція:
1. Використовуйте кнопку "step into" для введення функції getLanguage.
2. Повторно натисніть кнопку "step into" для проходу по коду з кроком в один рядок за один раз.
3. Спостерігайте за локальними змінними, щоб бачити, як вони змінюються в процесі виконання функції
Дійшовши до функції getLanguage, ви побачите, що вона намагається зчитувати мову з рядку user-agent. Автор цього коду помітив, що деяка інформація про мову включена в рядок user-agent в деяких браузерах, і намагається парсити navigator.userAgent щоб отримати інформацію:
Прибрати підсвітку коду
Проходячи по цьому коду за допомогою step в дебагері, ви можете використовувати огляд локальних змінних. На малюнку 6 показано Firebug і IE8 Developer Tools з розгорненим масивом ar1, для демонстрації елементів у ньому:

Малюнок 6: Панель локальних змінних Firebug і IE8, під час роботи функції getLanguage.
Вираз ar1[i].match(/^(.{2})$/) просто шукає рядок розміром в два символи, очікуючи зустріти два символи коду мови, як en або no. Тим не менше, як ви можете бачити на скріншоті, інформація про мову Firefox в рядку user-agent дорівнює nn-NO. В IE відсутня інформація що відносяться до мови, в цій частині рядка userAgent.
Другий баг був знайдений: виявлення мови шукає два символи, що позначають код мови, але Firefox містить п'ять букв рядка locale, а IE не має її взагалі. Цей код 'виявлення мови', по всій видимості, повинен бути зібраний разом і замінено на щось, що виконуються на стороні сервера і використовує header HTTP Accept-Language або, при можливості, читати navigator.language або navigator.userLanguage в IE. Приклад того, як ця функція може виглядати:
Прибрати підсвітку коду
Баг три: незрозуміле виникнення змінної prop


Малюнок 7: Панель локальних змінних в Firebug і Dragonfly показує глобальну змінну prop.
На малюнку 7, ви можете чітко бачити дивну змінну prop. У добре написаних додатках, кількість глобальних змінних зводиться до мінімуму, так як вони можуть призводити до скрутної ситуації, коли різні секції програми намагаються використовувати однакові імена для змінних. Уявіть, що завтра інша команда розробників додасть нову частину до нашого додатку і також назве її prop. Вийде, що дві різні частини програми намагаються використовувати одну і ту ж змінну для різних цілей. Така практика є керівництвом до конфліктів і появи багів. Отже, вам потрібно знайти, де ця змінна оголошена, і спробувати зробити її локальною. Ви могли б почати шукати її таким же чином, як ми вирішили другий баг, але є більш розумний спосіб.
Дебагери в багатьох інших мовах програмування мають концепцію 'watch', яка здатна сповіщати дебагер при зміни будь-якої змінної. На даний момент, ні Dragonfly, ні Firebug не підтримують 'watch', але можна легко домогтися аналогічного ефекту, додавши наступний рядок коду дебагера, угорі коду скрипта, який ви хочете відстежувати:
Прибрати підсвітку коду
Щоб додати цю "watch"-функціональність до скрипта:
1. Додайте вищевказаний код вгорі першого скрипта.
2. Перезавантажте сторінку.
3. Подивіться, як він реагує, при знаходженні проблеми.
При використанні getters і setters, можна зробити емуляцію функціональності 'watch' і допомогти у 'розумній' установці breakpoint'ів.
У IE8 Developer Tools є панель "watch", але вона не робить переривання, коли змінна була змінена. Та підтримка getters і setters, яка є в IE8, не дає можливості робити емуляцію функціональності тим способом, яким ви можете зробити це в Firefox, Opera і Safari.
Коли Ви оновите додаток, воно буде робити переривання, при оголошенні глобальної змінної prop. Взагалі-то, зупинка буде здійснена на коді, який викликає налагодження, тому що саме там йде вираз дебагера. Одне натискання на кнопку 'step out' і ви перенесетесь від функції setter до того місця, де була оголошена змінна. Цей код був знайдений всередині функції getElements:
Прибрати підсвітку коду
Тепер зупинка сталася в рядку, що починається з 'for (prop'. Тут ви можете бачити, що змінна prop використовується без попереднього оголошення як локальна змінна з ключовим словом var всередині функції. Її проста зміна на 'for (var prop', вирішить наш третій баг.
Висновки
Ця стаття демонструє основи використання дебагерів, а також деякі поглиблені техніки налагодження JavaScript. Ви вивчили як встановлювати точки зупинки у скрипту і дебагері, як робити покрокове виконання коду, як використовувати користувацький інтерфейс дебагеру.
Якщо у вас виникли труднощі в дотриманні інструкцій в цій статті, не турбуйтеся! Володіння початковими основами зробить вас кращим розробником і в кінцевому рахунку, ви розробите свої власні цікаві техніки, якими зможете поділитися.
Оригінал: Advanced Debugging with JavaScript
Про доступність
Ця стаття розповідає про сильні сторони і розходження між інструментами налагодження і показує, як ми можемо виконувати сучасні завдання налагодження в JavaScript. Наша методика включає в себе часте застосування миші; якщо ви віддаєте перевагу використовувати клавіатуру, або ж покладайтеся на допоміжні інструменти, як зчитувачів екранів, для роботи з цими інструментами вам варто звернутися до відповідної документації, щоб дізнатися як (або якщо) ці інструменти будуть працювати.
Дебагери
Зі зростаючим вибором хороших доступних дебагерів, Javascript-програмісти можуть досягти більшого, якщо вони навчаться ними правильно користуватися. Користувацький інтерфейс дебагеру Javascript (UIs) стає краще, серед продуктів з'явилося більше стандартів, їм стало легше користуватися і таким чином, стає простіше, як експертам, так і новачкам у вивченні процесу налагодження в JavaScript.
На даний момент, інструменти налагодження існують для всіх основних веб-браузерів:
• Для Firefox існує добре відоме розширення Firebug.
• IE8 (в бета релізі, на момент написання цієї статті) виходить з вбудованим Developer Tools.
• Opera (версія 9.5 і вище) підтримує дебагер Opera Dragonfly
• У Safari є і дебагер Drosera JS і DOM viewer, що називається WebInspector. У більш пізніх версіях Safari, дебагер вбудований в WebInspector.
На даний момент, Firebug і Dragonfly є найбільш стабільними варіантами вибору. Інструменти бета версії IE8 іноді ігнорують точки зупинки (breakpoints), і на час написання цієї статті, схоже, що у WebInspector є проблеми з сумісністю Webkit.
Ознайомтеся з різними інструментами налагодження - ви ніколи не знаєте, в якому браузері з'явиться наступний баг. Так як дебагери мало чим відрізняються між собою у функціональності, легко перемикатися між ними, якщо ви вивчили хоча б один з них.
Процес налагодження
Коли ви розбираєтесь з певною проблемою, ви зазвичай слідуєте таким крокам:
1. Знаходите відповідний код, панелі перегляду коду дебагеру.
2. Встановлюєте точки зупинки (breakpoints) де може виникнути помилка.
3. Якщо це вбудований скрипт, перезапускаєте його, оновленням сторінки в браузері.
4. Чекаєте доки дебагер не призупинить виконання і не дасть можливість переглянути код крок за кроком.
5. Переглядаєте значення змінних. Наприклад, шукаєте ті змінні, які не визначені, але повинні містити значення, або повертають "false", у той час як очікується "true".
6. При необхідності, використовуєте командний рядок для виконання коду або зміни значень змінних для тестування.
7. Знаходите проблему, виділивши ту частину коду або ввід, які викликають помилку.
Щоб створити точку зупинки ви також можете додати код дебагеру до вашого коду:
Прибрати підсвітку коду
1 2 3 4 5 | function frmSubmit(event){ event = event || window.event; debugger; var form = this; } |
Вимоги дебагеру
Більшість дебагерів вимагають добре відформатований код. Скрипти, написані в одну лінію, ускладнюють процес пошуку помилок у по-рядкових дебагерах. Заплутаний код складно налагоджувати, особливо 'запакований', що вимагає розпаковування через eval(). Безліч бібліотек Javascript дозволяють вам вибирати між упакованим/заплутаним і добре відформатованим варіантами, даючи можливість використовувати відформатований код для налагодження.
Демонстрація налагодження
Давайте почнемо з невеликого, повного багів прикладу, для того щоб навчитися як знаходити і виправляти кожну неполадку по черзі. Прикладом є екран входу в веб-додатку.
Уявіть, що ви працюєте з цим шикарним новим веб-додатком, і ваші тестери просять вас розглянути такий список багов:
1. Повідомлення "Loading ..." в статус барі не зникає після повного завантаження програми.
2. Мова за замовчуванням є норвезька, навіть в англійських версіях Firefox і IE.
3. Десь виникає глобальна змінна prop.
Запуск дебагерів
• В Firefox вам необхідно впевнитися, що у вас встановлено розширення Firebug. Для запуску виберіть Select "Tools> Firebug> Open Firebug".
• У Opera 9.5 beta 2 і пізніше, виберіть "Tools> Advanced> Developer Tools."
• У IE8 beta, виберіть "Tools> Developer Tools."
• У Safari або WebKit, спочатку виберіть налагодження, а потім виберіть "Debug> Show Web Inspector."
Настав час запустити дебагери. Щоб слідувати демонстрації, приділіть увагу до поетапної інструкції, представленій у цій статті. Так як деякі інструкції включають в себе зміну коду, можливо, ви захочете зберегти сторінку локально і завантажити її з вашої файлової системи до початку роботи.
Баг перший: повідомлення "Loading ..." в статус барі.
Якщо ви подивитеся на додаток в Dragonfly і Firebug, ви отримаєте такий початковий вигляд, як на малюнку 1.


Малюнок 1: початковий вигляд нашої програми JavaScript у Dragonfly та Firebug відповідно.
Коли ви подивитеся на вихідний код у дебагері, зауважте, що там є функція clearLoadingMessage(), оголошена на початку коду. Це місце здається підходящим для точки зупинки (breakpoint). І ось як ми це робимо:
1. Клацніть на номер рядка в лівому полі для встановлення breakpoint в першому рядку всередині функції clearLoadingMessage().
2. Перезавантажте сторінку.
Примітка: точка зупинки повинна бути встановлена на рядок коду, який буде запускати функцію. Рядок, що містить function clearLoadingMessage () (є всього лише оголошенням функції. Установка breakpoint в цьому місці ніколи не викличе зупинку дебагеру. Замість цього встановіть точку зупинки на перший рядок всередині функції. Коли ви перезавантажите сторінку, запуск скрипта буде зупинено на breakpoint і ви побачите вивід, приблизно як на малюнку 2. (Dragonfly показаний зверху, Firebug внизу)


Малюнок 2: дебагер зупинився на breakpoint всередині clearLoadingMessage.
Давайте крок за кроком розберемо функцію. Ви бачите, що вона змінила два DOM елементи, і в рядку 28 згадується слово statusbar, що виглядає як знак. Щось на зразок такого getElements ( 'p', ( 'class':' statusbar ')) [0] знайде елемент statusbar в DOM. Чи є спосіб швидко перевірити цю теорію?
Вставте відповідний блок у командний рядок для того, щоб перевірити вашу теорію. На малюнку 3 показано три знімка екранів (Dragonfly, Firebug і IE8) після зчитування innerHTML або outerHTML елемента, повертається командою про яку сказано вище.
Для тестування цього припущення:
1. Знайдіть командний рядок:
В Firebug, перейдіть на вкладку "Console".
В Dragonfly, подивіться під панеллю вихідного коду JavaScript.
В IE8 Developer Tools, праворуч знайдіть вкладку "Console."
2. Вставте getElements ( 'p', ( 'class':' statusbar ')) [0]. InnerHTML в командний рядок.
3. Натисніть ввід.



Малюнок 3: вивід показаний у Dragonfly, Firebug і IE8, відповідно.
Командний рядок є зручним інструментом, що дозволяє швидко тестувати невеликі блоки коду. Вбудована консоль Firebug дуже зручна - якщо вивід команди є об'єктом, ви отримуєте зрозумілий вид. Наприклад, якщо це об'єкт DOM, ви отримуєте відповідну розмітку.
Ви можете використовувати командний рядок для глибокого розгляду проблеми. Розглянутий рядок JavaScript робить наступні три речі:
1. Отримує посилання на елемент statusbar. У DOM inspector view ви побачите, що відповідна розмітка це <p class='statusbar'>
2. Показує, що це firstChild, іншими словами, перший елемент всередині елемента параграф.
3. Встановлює властивість innerText.
Давайте спробуємо запустити трохи більше, ніж цю команду, з командного рядка. (Підказка: використовуйте кнопку 'стрілка вверх', для навігації назад до попередніх команд, які набиралися в полі командного рядка). Наприклад, ви можете дізнатися, яке поточне значення властивості innerText елемента, до того як воно встановлюється. Щоб перевірити це, ви можете набрати наступну команду:
Прибрати підсвітку коду
1 | getElements( 'p', {'class':'statusbar'} )[0].firstChild.innerText |
На диво, виведе ... нічого. Отже, вислів getElements( 'p', {'class':'statusbar'} )[0].firstChild відноситься до чогось в DOM, що не містить тексту або не має властивості innerText.
Отже, наступне запитання: що саме є першою нодою в елементі параграфа? Давайте поставимо це питання до командного рядка. (Результат дивіться на малюнку 4)

Малюнок 4: Командний рядок дебагера Dragonfly, виводить [object Text].
Вивід [object Text] у Dragonfly показує, що цей текстова нода DOM. Firebug показує вміст текстової ноди як посилання на оглядач DOM. Тепер ви знайшли проблему, яка пояснює перший баг: текстові ноди не мають властивості innerText - вони є тільки у Нод елементів. Отже, установка p.firstChild.innerText нічого не робить. Цей баг може бути усунутий заміною innerText на nodeValue, що є властивістю, яке визначено стандартом W3C DOM для Нод тексту.
Після чого у вас є можливість переглянути цей приклад:
1. Натисніть [F5] або кнопку запуску, для завершення скрипта, коли проблема вже була виявлена.
2. Не забудьте прибрати всі старі точки зупинки, повторним натисканням на номери рядків.
Баг другий: виявлення проблемної мови
Ви могли помітити рядок var lang; / * language * / вгорі JavaScript - код, що встановлює цю змінну, мабуть, пов'язаний, коли виконання відбувається невірно. Ви можете швидко знаходити певні речі в коді, використовуючи ручні функції пошуку, представлені обома дебагерами. У Dragonfly це знаходиться відразу над переглядом коду; в Firebug це в правому верхньому кутку користувацького інтерфейсу (дивіться малюнок 5).
Для знаходження місць, які мають відношення до локалізації:
1. Наберіть 'lang =' у рядку пошуку.
2. Встановіть точку зупинки у рядку, де призначається значення для змінної lang.
3. Перезавантажте сторінку.
WebInspector Safari також має дуже великі можливості пошуку. WebInspector дозволяє шукати по всьому коду, включаючи розмітку, CSS і JavaScript. Результати відображаються в окремій панелі, де ми можете перейти до вихідного коду за допомогою подвійного кліка, як показано на скріншоті.


Малюнок 5: пошук за допомогою дебагерів Dragonfly і WebInspector.
Для відстеження, що робить ця функція:
1. Використовуйте кнопку "step into" для введення функції getLanguage.
2. Повторно натисніть кнопку "step into" для проходу по коду з кроком в один рядок за один раз.
3. Спостерігайте за локальними змінними, щоб бачити, як вони змінюються в процесі виконання функції
Дійшовши до функції getLanguage, ви побачите, що вона намагається зчитувати мову з рядку user-agent. Автор цього коду помітив, що деяка інформація про мову включена в рядок user-agent в деяких браузерах, і намагається парсити navigator.userAgent щоб отримати інформацію:
Прибрати підсвітку коду
1 2 3 4 5 6 7 | var str1 = navigator.userAgent.match( /\((.*)\)/ )[1]; var ar1 = str1.split(/\s*;\s*/), lang; for (var i = 0; i < ar1.length; i++){ if (ar1[i].match(/^(.{2})$/)){ lang = ar1[i]; } } |
Проходячи по цьому коду за допомогою step в дебагері, ви можете використовувати огляд локальних змінних. На малюнку 6 показано Firebug і IE8 Developer Tools з розгорненим масивом ar1, для демонстрації елементів у ньому:

Малюнок 6: Панель локальних змінних Firebug і IE8, під час роботи функції getLanguage.
Вираз ar1[i].match(/^(.{2})$/) просто шукає рядок розміром в два символи, очікуючи зустріти два символи коду мови, як en або no. Тим не менше, як ви можете бачити на скріншоті, інформація про мову Firefox в рядку user-agent дорівнює nn-NO. В IE відсутня інформація що відносяться до мови, в цій частині рядка userAgent.
Другий баг був знайдений: виявлення мови шукає два символи, що позначають код мови, але Firefox містить п'ять букв рядка locale, а IE не має її взагалі. Цей код 'виявлення мови', по всій видимості, повинен бути зібраний разом і замінено на щось, що виконуються на стороні сервера і використовує header HTTP Accept-Language або, при можливості, читати navigator.language або navigator.userLanguage в IE. Приклад того, як ця функція може виглядати:
Прибрати підсвітку коду
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | function getLanguage() { var lang; if (navigator.language) { lang = navigator.language; } else if (navigator.userLanguage) { lang = navigator.userLanguage; } if (lang && lang.length > 2) { lang = lang.substring(0, 2); } return lang; } |
Баг три: незрозуміле виникнення змінної prop


Малюнок 7: Панель локальних змінних в Firebug і Dragonfly показує глобальну змінну prop.
На малюнку 7, ви можете чітко бачити дивну змінну prop. У добре написаних додатках, кількість глобальних змінних зводиться до мінімуму, так як вони можуть призводити до скрутної ситуації, коли різні секції програми намагаються використовувати однакові імена для змінних. Уявіть, що завтра інша команда розробників додасть нову частину до нашого додатку і також назве її prop. Вийде, що дві різні частини програми намагаються використовувати одну і ту ж змінну для різних цілей. Така практика є керівництвом до конфліктів і появи багів. Отже, вам потрібно знайти, де ця змінна оголошена, і спробувати зробити її локальною. Ви могли б почати шукати її таким же чином, як ми вирішили другий баг, але є більш розумний спосіб.
Дебагери в багатьох інших мовах програмування мають концепцію 'watch', яка здатна сповіщати дебагер при зміни будь-якої змінної. На даний момент, ні Dragonfly, ні Firebug не підтримують 'watch', але можна легко домогтися аналогічного ефекту, додавши наступний рядок коду дебагера, угорі коду скрипта, який ви хочете відстежувати:
Прибрати підсвітку коду
1 | __defineSetter__('prop', function() { debugger; }); |
Щоб додати цю "watch"-функціональність до скрипта:
1. Додайте вищевказаний код вгорі першого скрипта.
2. Перезавантажте сторінку.
3. Подивіться, як він реагує, при знаходженні проблеми.
При використанні getters і setters, можна зробити емуляцію функціональності 'watch' і допомогти у 'розумній' установці breakpoint'ів.
У IE8 Developer Tools є панель "watch", але вона не робить переривання, коли змінна була змінена. Та підтримка getters і setters, яка є в IE8, не дає можливості робити емуляцію функціональності тим способом, яким ви можете зробити це в Firefox, Opera і Safari.
Коли Ви оновите додаток, воно буде робити переривання, при оголошенні глобальної змінної prop. Взагалі-то, зупинка буде здійснена на коді, який викликає налагодження, тому що саме там йде вираз дебагера. Одне натискання на кнопку 'step out' і ви перенесетесь від функції setter до того місця, де була оголошена змінна. Цей код був знайдений всередині функції getElements:
Прибрати підсвітку коду
1 2 | for (prop in attributes) { if (el.getAttribute(prop) != attributes[prop]) includeThisElement = false; |
Тепер зупинка сталася в рядку, що починається з 'for (prop'. Тут ви можете бачити, що змінна prop використовується без попереднього оголошення як локальна змінна з ключовим словом var всередині функції. Її проста зміна на 'for (var prop', вирішить наш третій баг.
Висновки
Ця стаття демонструє основи використання дебагерів, а також деякі поглиблені техніки налагодження JavaScript. Ви вивчили як встановлювати точки зупинки у скрипту і дебагері, як робити покрокове виконання коду, як використовувати користувацький інтерфейс дебагеру.
Якщо у вас виникли труднощі в дотриманні інструкцій в цій статті, не турбуйтеся! Володіння початковими основами зробить вас кращим розробником і в кінцевому рахунку, ви розробите свої власні цікаві техніки, якими зможете поділитися.
Оригінал: Advanced Debugging with JavaScript
Рейтинг:




<< Ви можете поставити оцінку цій статтіПодібні статті:
Розуміння області видимості в об’єктно-орієнтованому JavaScript
Визначення і використання власних подій в Javascript