En detaljerad guide till felsökning av JavaScript-kod i Chrome Devtools. Stoppar när DOM ändras
Föreställ dig att du arbetar med denna otroliga nya webbapplikation och dina testare har bett dig att fixa följande buggar:
- I Firefox måste du se till att du har installerat Firebug-tillägg. Välj "Verktyg > Firebug > Öppna Firebug".
- I Opera 9.5+ väljer du "Verktyg > Avancerat > Utvecklingsverktyg."
- I IE beta, gå till "Verktyg > Paneler > Utforskarfält > IE Developer Toolbar."
- I Safari eller WebKit, aktivera först felsökningsmenyn (1), välj sedan "Utveckla > Visa webbinspektör"
ris. 1: Inledande vy av vår JavaScript-applikation i Dragonfly respektive Firebug.
När du tittar på källkoden i debuggern, lägg märke till clearLoadingMessage()-funktionen i början av koden. Det här är ett bra ställe för en checkpoint.
Så här installerar du det:
När sidan laddas om kommer skriptet att sluta köras och du kommer att se vad som visas i figur två.
ris. 2: debuggers stannade vid Kontrollpunkt inuti clearLoadingMessage.
Låt oss ta en titt på funktionskoden. Som du lätt kan se uppdaterar den två DOM-element och rad 31 nämner ordet statusfält. Det ser ut som att getElements("p", ("class":"statusrad")).innerHTML letar efter statusfältselementet i DOM-trädet. Hur kan vi snabbt testa vårt antagande?
Klistra in detta uttalande i kommandoraden för att testa. Figur tre visar tre skärmdumpar (Dragonfly, Firebug och IE8) efter att ha läst innerHTML eller yttre HTML-element, returneras av kommandot du söker efter.
Gör följande för att kontrollera:
* I Firebug byter du till fliken "Konsol".
* I Dragonfly, titta under JavaScript-kodpanelen.
* I IE8, hitta fliken "Konsoll" till höger.
ris. 3: Mata ut kommandoresultatet i Dragonfly, Firebug respektive IE8.
Kommandoraden är mycket användbart verktyg, vilket gör att du snabbt kan testa små kodbitar. Firebug-konsolintegrationen är mycket användbar - om ditt kommando matar ut ett objekt får du en mycket intelligent vy. Till exempel, om det är ett DOM-objekt - kommer du att se det uppmärkta resultatet.
Du kan använda konsolen för att göra mer djupgående forskning. JavaScript-sträng, som vi studerar, gör följande tre saker:
Överraskning, på slutet... ingenting. Således pekar uttrycket getElements("p",("class:"statusbar")).firstChild på något objekt i DOM som inte innehåller någon text, eller som inte har en innerText-egenskap.
Sedan är nästa fråga: vad kommer egentligen först? barnelement vid stycket? Låt oss ställa den här frågan på kommandoraden. (Se fjärde bilden).
ris. 4: kommandorad StDragonfly debugger, utgång [Textobjekt].
Dragonflys debugger-utgång - [Textobjekt] visar att detta är en DOM-textnod. Därmed hittade vi orsaken till det första problemet. En textnod har inte en innerText-egenskap, därför gör det ingenting att sätta p.firstChild.innerText till ett värde. Detta fel kan enkelt fixas genom att ersätta innerText med nodeValue, som är en egenskap som definieras av W3C-standarden för textnoder.
Nu när vi har tagit itu med det första felet:
Gör följande för att ta reda på var lokaliseringsproblemet sannolikt uppstår:
ris. 5: Sök i Dragonfly och WebInspector.
För att kontrollera vad den här funktionen gör:
var str1 = navigator.userAgent.match(/\((.*)\)/);
var ar1 = str1.split(/\s*;\s*/), lang;
för (var i = 0; i< ar1.length; i++){
if (ar1[i].match(/^(.(2))$/))(
lang = ar1[i];
}
}
När du går igenom din kod kan du använda Local Variables Viewer. Figur 6 visar hur det ser ut i Firebug och IE8 DT vi utökade ar1-arrayen för att se dess element.
ris. 6: Ruta för att visa lokala variabler för getLanguage-funktionen i Firebug IE8
Uttrycket ar1[i].match(/^(.(2))$/) söker helt enkelt efter en sträng som består av två tecken, till exempel "nej", "en". Men som du kan se på skärmdumpen i Firefox presenteras information om språket i formen "nn-NO" (2). IE lägger inte alls in språkinformation i användaragenten.
Således hittade vi det andra felet: språket bestämdes genom att söka efter en tvåbokstavskod i användaragentraden, men Firefox har en språkbeteckning på fem tecken, och IE har det inte alls. Sådan kod måste skrivas om och ersättas med språkdetektering antingen på serversidan med hjälp av Accept-Language HTTP-headern eller genom att hämta den från navigator.language (navigator.userLanguage för IE). Här är ett exempel på vad en sådan funktion kan vara
funktion getLanguage() (var lang;
if (navigator.language) (
lang = navigator.språk;
) else if (navigator.userLanguage) (
lang = navigator.userLanguage;
}
if (lang && lang.length > 2) (
lang = lang.substring(0, 2);
}
return lang;
}
Misstag tre: Den mystiska "prop"-variabeln
ris. 7: Den globala prop-variabeln är synlig i Firebug och Dragonfly-variabelvypanelen
I figur 7 kan du tydligt se variabeln "prop". I välskrivna applikationer bör antalet globala variabler hållas till ett minimum, eftersom de kan orsaka problem när till exempel två delar av applikationen vill använda samma variabel. Låt oss anta att i morgon kommer ett annat team att lägga till nya funktioner till vår applikation och även deklarera variabeln "prop". Vi kommer att sluta med två olika delar av applikationskoden som använder samma namn för olika saker. Denna situation leder ofta till konflikter och misstag. Du kan försöka hitta denna variabel och deklarera den lokal. För att göra detta kan du använda sökningen, som vi gjorde i föregående fall, men det finns ett smartare sätt...
Debuggers för många andra programmeringsspråk har konceptet med en "klocka", som går in i felsökningsläge när en specificerad variabel ändras. Varken Firebug eller Dragonfly stöder "observatörer" för närvarande, men vi kan enkelt emulera liknande beteende genom att lägga till nästa rad till början av koden som studeras:
__defineSetter__("prop" , function () (debugger; ));Gör följande:
När du laddar om sidan kommer kodexekveringen omedelbart att stoppas där variabeln "prop" är definierad. Det faktiska stoppet kommer att inträffa vid den punkt där du lade till raden ovan. Ett klick på "steg ut"-knappen tar dig till platsen där variabeln är inställd.
för (prop i attribut) (if (el.getAttribute(prop) != attribut) includeThisElement = false ;
Det är inte svårt att lägga märke till för slinga där prop-variabeln deklareras utan nyckelord var, dvs. global. Att fixa detta är inte svårt, lägg bara till var och fixa felet: "clone"-attributet, som inte borde finnas där. Det fjärde felet upptäcktes tydligen av en avancerad testare som använder DOM-inspektören, eftersom dess existens inte visas. på något sätt in användargränssnitt applikationer. Om vi öppnar DOM-inspektören (i Firebug är detta fliken "HTML", i Dragonfly kallas det "DOM"), kommer vi att se att många element har klon attribut, som inte borde finnas.
ris. 8: Dragonflys DOM-inspektör visar problematisk kod.
Eftersom detta inte påverkar applikationens användare på något sätt, kan det här felet inte betraktas som allvarligt, men glöm inte att det kan påverka prestandan avsevärt, eftersom skriptet ställer in attributet på hundratals och tusentals element.
Mest snabbt sätt Lösningen på detta problem är att ställa in en brytpunkt som aktiveras när ett attribut som kallas clone sätts på något HTML-element. Kan debuggers göra detta?
JavaScript är ett mycket flexibelt språk, och ett av dess styrkor(eller svaga, beroende på din synvinkel) är vad du kan ersätta grundläggande funktioner språk med ditt eget. Lägg till denna kodbit på sidan, den kommer att åsidosätta systemmetod setAttribute, vilket gör att koden slutar när egenskapen "clone" är inställd:
Element.prototype.setAttribute = funktion (namn, värde) (
if (namn == "klon") (
felsökare; /* stoppa skriptet */
}
funcSetAttr.call(detta, namn, värde); /* anropa en tidigare sparad systemmetod så att normala egenskaper ställs in korrekt */
};
Så vi gör följande:
När exekveringen stannar vid en brytpunkt, vill du veta var setAttribute()-anropet inträffade, vilket betyder att du måste gå tillbaka upp i funktionsanropskedjan och se vad som händer där. Du kan använda en samtalsstack för detta.
ris. 9: Ring stack i Dragonfly och IE8.
Figur 10 visar stacken i Firebug. I kö " setAttribute