最新のJavaScriptデバッグ

優れたデバッガーが幅広く選択されているため、JavaScriptプログラマーはそれらの使用方法を学ぶことで多くのメリットを得ることができます。 ユーザーインターフェイスはより洗練され、標準化され、使いやすくなっているため、エキスパートとJSデバッグの初心者の両方に役立ちます。 この記事では、一般的なWebアプリケーションを使用してエラーを診断および分析するための高度なデバッグ手法について説明します。

現在、すべての主要なブラウザでデバッグツールを使用できます。

FirebugとDragonflyは現在最も安定しています。 IE8ユーティリティはブレークポイントを無視する場合があり、この記事の執筆時点では、WebInspectorには最新のWebkitビルドとの互換性の問題があります。

デバッグツールを学習します。どのブラウザで次のエラーが発生するかはわかりません。 デバッガーの機能はほぼ同等であるため、少なくとも1つを使用する方法がわかったらすぐに切り替えることができます。


デバッグ技術。


特定の問題にぶつかった場合、通常は次の手順を実行します
  1. デバッガーのコードビューペインで適切なコードを見つける
  2. 潜在的に問題があると思われる場所にブレークポイントまたはポイントを設定します
  3. ページをリロードするか、イベントハンドラーである場合はボタンをクリックして、スクリプトを再度実行します。
  4. デバッガーの実行が停止し、ステップバイステップのコード実行が有効になるのを待ちます。
  5. 変数の値を調べます。 たとえば、何らかの理由で未定義の変数を探します。ただし、値を含む必要があるか、「false」を返す必要がありますが、「true」を返すと予想されます。
  6. 必要に応じて、コマンドラインを使用してコードを計算するか、変数の値を変更します
  7. 問題の原因となったコードの部分を調べて問題を見つけ、排除します。

ブレークポイントを作成するには、特別なデバッガー命令をコードに追加することもできます。
function frmSubmit( event ){
event = event || window. event ;
debugger;
var form = this ;
}


必要条件


ほとんどのデバッガーでは、コードを適切にフォーマットする必要があります。 「1行で」記述されたスクリプトは、行ごとのデバッガーでのエラーの検出を妨げます。 難読化されたコードはデバッグするのが非常に難しく、特にパックされています。 多くのJavaScriptライブラリを使用すると、パッケージ化されたバージョンと難読化されたバージョン、または通常の形式のバージョンを選択できます。もちろん、最新のスクリプトをデバッグする必要があります。

デバッグデモ


間違いを見つけて修正する方法を学ぶために、小さな、特に「思いやりのある」例から始めましょう。 私たちの例はログインページです。

この信じられないほどの新しいWebアプリケーションに取り組んでおり、テスターから次のエラーの修正を求められたと想像してください。
  1. 「読み込み中...」アプリケーションの読み込みが終了しても、ステータスバーのメッセージは消えません。
  2. IEおよびFirefoxの英語バージョンでも、デフォルトの言語はノルウェー語です。
  3. コードのどこかで、グローバル変数propが形成されました(悪-約Per。)。
  4. 何らかの理由で、DOMビューアでは、すべての要素に属性「クローン」があります。

デバッガーの実行



デバッガーを実行します。 一部の手順ではコードを変更する必要があるため、テストページを保存し、ディスクからブラウザにロードする方が適切です。

エラー番号1:メッセージ「読み込み中...」


デバッグされたアプリケーションを見ると、最初に図1に示されているものが表示されます。


1:それぞれDragonflyとFirebugでのJavaScriptアプリケーションの最初の外観。

デバッガでソースコードを見るときは、コードの最初にあるclearLoadingMessage()関数に注意してください。 これはブレークポイントに適した場所です。

置き方:
  1. 行番号の左側のフィールドをクリックして、clearLoadingMessage()関数の最初の行にブレークポイントを設定します。
  2. ページをリロードします。

注意:ブレークポイントは、関数の起動時に実行されるコードの行に設定する必要があります。 clearLoadingMessage()を含む文字列{これは単なる関数定義であるため適合しません。 ここにブレークポイントを設定すると、デバッガーはそこで停止しません。代わりに、関数内にブレークポイントを設定する必要があります。

ページがリロードされると、スクリプトの実行が停止し、図2に示す内容が表示されます。



2:デバッガーはclearLoadingMessage内のブレークポイントで停止しました。

関数コードを見てみましょう。 ご覧のとおり、2つのDOM要素が更新され、31行目にstatusbarという単語が記載されています。 getElements( 'p'、{'class': 'statusbar'})のように見えます[0] .innerHTMLはDOMツリーでstatusbar要素を探します。 仮定をどのようにすばやくテストしますか?

確認するには、この指示をコマンドラインに貼り付けます。 図3は、調べているコマンドによって返されたinnerHTML要素またはouterHTML要素を読み取った後の3つのスクリーンショット(Dragonfly、Firebug、およびIE8)を示しています。

確認するには、次を実行します。
  1. コマンドラインを見つけます:
    * Firebugで、「コンソール」タブに切り替えます。
    * Dragonflyでは、JavaScriptコードバーの下を表示します。
    * IE8では、右側の「コンソール」タブを見つけます。
  2. getElements( 'p'、{'class': 'statusbar'})[0] .innerHTMLをコマンドラインに挿入します。
  3. Enterキーを押します。




3:Dragonfly、Firebug、およびIE8でのコマンド出力。

コマンドラインは、コードの小さな部分をすばやく確認できる非常に便利なツールです。 Firebugコンソールの統合は非常に便利です。チームがオブジェクトを表示すると、非常にインテリジェントなビューが得られます。 たとえば、DOMオブジェクトの場合、マークアップされた結果が表示されます。

コンソールを使用して、より詳細な調査を行うことができます。 学習しているJavaScript文字列は、次の3つのことを行います。
  1. statusbar要素への参照を取得します。
  2. firstChild、つまりこの段落の最初のノードを見つけます。
  3. innerTextプロパティを設定します。

前のコマンド以外の、コンソールで実行してみましょう。 たとえば、新しい値が割り当てられる前に、innerTextプロパティの現在の値が何かを調べることができます。 調べるために、コマンドラインの「=」記号までコマンド全体を入力できます。

getElements( 'p' , { 'class' : 'statusbar' } )[0].firstChild.innerText

驚き、出力...何もない。 したがって、式getElements( 'p'、{'class:' statusbar ''})[0] .firstChildは、テキストを含まないか、innerTextプロパティを持たないDOM内の何らかのオブジェクトを指します。

次に、次の質問:段落の最初の子は何ですか? コマンドラインでこの質問をしてみましょう。 (4番目の図を参照)。


4:デバッガーSt Dragonflyのコマンドライン、[テキストオブジェクト]を出力します。

Dragonflyのデバッガー出力-[テキストオブジェクト]は、これがDOMテキストノードであることを示しています。 したがって、最初の問題の原因が見つかりました。 テキストノードにはinnerTextプロパティがないため、p.firstChild.innerTextの値を設定しても何も実行されません。 このエラーは、innerTextをnodeValueに置き換えることで簡単に修正できます。nodeValueは、テキストノードのW3C標準で定義されているプロパティです。

さて、最初のエラーを見つけた後:
  1. [F5]またはRunボタンを押して、スクリプトを終了します。
  2. 行番号をもう一度クリックして、設定したコントロールポイントをリセットすることを忘れないでください。

間違い2:言語を決定する問題。


lang; / * language * /変数がスクリプトの先頭にあることに気付いたかもしれません。 この変数の値を設定するコードが問題の原因であると想定できます。 デバッガーに組み込まれている検索機能を使用して、このコードを見つけることができます。 Dragonflyでは、検索はコードビューアーのすぐ上、Firebugでは右上隅にあります(図5を参照)。

ローカリゼーションの問題が発生する可能性がある場所を見つけるには、次の手順を実行します。
  1. 検索ボックスに「lang =」と入力します。
  2. lang変数が設定されている行にブレークポイントを設定します。
  3. ページをリロードします。

WebInspectorには非常に便利な検索機能もあります。 ページレイアウト、CSS、およびJavaScriptコードで同時に検索できます。 スクリーンショットに示すように、結果は別のパネルに表示され、そこでダブルクリックして目的の場所に移動できます。



5:DragonflyとWebInspectorを検索します。

この関数の機能を確認するには:
  1. [ステップイン]ボタンをクリックして、getLanguage関数を入力します。
  2. 何度も何度もクリックして、コードをステップごとに実行します
  3. 変数表示ウィンドウで、値の変化を確認します。

関数を入力すると、navigator.userAgentを分析して、ブラウザーのユーザーエージェントの行から言語を読み取ろうとする試みが表示されます。
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];
}
}


ステップごとのコードの受け渡しのプロセスで、ローカル変数を表示するためにウィンドウを使用できます。 図6は、FirebugおよびIE8 DTでの外観を示しています。ar1配列を拡張して、その要素を確認します。


6:Firebug IE8のFirebug IE8のローカル変数ビューアー

式ar1 [i] .match(/ ^(。{2})$ /)は、「no」、「en」などの2文字で構成される文字列を単に検索します。 ただし、Firefoxのスクリーンショットが示すように、言語情報は「nn-NO」 (2)として表示されます。 IEは、ユーザーエージェントに言語情報を一切入力しません。

したがって、2番目のエラーが見つかりました。言語はユーザーエージェント文字列で2文字のコードを検索して決定されましたが、Firefoxには5文字の言語指定があり、IEにはまったくありません。 このようなコードは、サーバー側でAccept-Language HTTPヘッダーを使用するか、navigator.language(IEの場合はnavigator.userLanguage)から取得することにより、書き換えて言語定義に置き換える必要があります。 このような関数が何であるかの例を次に示します。

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;
}


間違い3:神秘的な変数「prop」



7:グローバル変数propは、FirebugおよびDragonflyの変数ビューペインに表示されます

図7では、変数「prop」が明確に表示されています。 適切に記述されたアプリケーションでは、グローバル変数の数は最小限にする必要があります。たとえば、アプリケーションの2つの部分で同じ変数を使用したい場合に問題が発生する可能性があるためです。 明日、別のチームがアプリケーションに新しい機能を追加し、変数「prop」を宣言するとします。 異なるものに同じ名前を使用して、2つの異なるアプリケーションコードを取得します。 この状況は、多くの場合、競合とエラーにつながります。 この変数を見つけて、ローカルで宣言することができます。 前のケースで行ったように、この検索を使用できますが、よりスマートな方法があります...

他の多くのプログラミング言語のデバッガーには、特定の変数が変更されたときにデバッグモードになる「ウォッチ」という概念があります。 FirebugもDragonflyも現時点では「オブザーバー」をサポートしていませんが、調査中のコードの先頭に次の行を追加することで、同様の動作を簡単にエミュレートできます。

__defineSetter__( 'prop' , function () { debugger; });

以下を実行します。
  1. 最初のスクリプトの先頭にデバッグコードを追加します。
  2. ページをリロードします。
  3. スクリプトがどのように中断されるかに注意してください。

IE8 DTには「ウォッチ」タブがありますが、変数が変更されても中断はありません。 したがって、この例はFirefox、Opera、およびSafariでのみ機能します。

ページをリロードすると、「prop」変数が定義されている場所でコードの実行が直ちに停止します。 実際、上記の行を追加した場所で停止します。 「ステップアウト」ボタンを1回クリックすると、変数が設定されている場所に移動します。

for (prop in attributes) {
if (el.getAttribute(prop) != attributes[prop]) includeThisElement = false ;


変数propがvarキーワードなしで宣言されているforループを簡単に見ることができます。 グローバル。 これを修正するのは難しくなく、varを追加してエラーを修正するだけです。

間違い4:クローン属性。


4番目のエラーは、DOMインスペクターを使用する上級テスターに​​よって明らかに発見されました。その存在は、アプリケーションのユーザーインターフェイスに現れないためです。 DOMインスペクターを開くと(Firebugでは「HTML」タブ、Dragonflyでは「DOM」と呼ばれます)、多くの要素にclone属性がありますが、そうではないはずです。


8:DragonflyのDOMインスペクターに問題コードが表示されます。

これはアプリケーションのユーザーにはまったく影響を与えないため、このバグを深刻なものと見なすことはできませんが、スクリプトが数百および数千の要素に属性を設定するため、パフォーマンスに大きな影響を与えることを忘れないでください。

この問題を見つける最も速い方法は、HTML要素にcloneという属性が設定されたときに起動するブレークポイントを設定することです。 デバッガーはこれを行うことができますか?

JavaScriptは非常に柔軟な言語であり、その長所(またはあなたの視点に応じて弱点)の1つは、言語の基本機能を独自のものに置き換えることができることです。 このコードをページに追加すると、setAttributeシステムメソッドがオーバーライドされ、「クローン」プロパティが設定されたときにコードが停止します。
var funcSetAttr = Element.prototype.setAttribute; /* */
Element.prototype.setAttribute = function (name, value) {
if (name == 'clone' ) {
debugger; /* */
}
funcSetAttr.call( this ,name,value); /* , */
};


そのため、以下を実行します。
  1. 上記のコードをページの最初のスクリプトの先頭に追加します。
  2. ページをリロードします。

再起動後、スクリプトはDOMツリーの処理を開始しますが、「bad」属性が設定されるとすぐに停止します。 (Firefoxの現在のバージョンでは、setAttributeの実装は要素ごとに異なります。上記のコードは常にOperaでのみ動作します。Firefoxで同じ効果を得るには、Elementという単語をHTMLFormElementに置き換えてより具体的なものをオーバーライドできますHTMLFormElement.prototype.setAttributeメソッド)。

実行がブレークポイントで停止すると、setAttribute()呼び出しが発生した場所を見つける必要があります。つまり、関数呼び出しチェーンの上位レベルに戻り、そこで何が起こるかを確認する必要があります。 これには呼び出しスタックを使用できます。


9:DragonflyおよびIE8のスタックを呼び出します。

図10は、Firebugのスタックを示しています。 ファイル名の横の「 setAttribute <makeElement <init 」行で、左端が現在の関数です。


10:Firebugの呼び出しスタック。 左側で呼び出された最新の関数。

スタック内の関数の名前をクリックすると、その時点でどのように終わったかを判断できます。 どのように機能するかを理解するために、自分で試してみることが非常に重要です。 スタックに沿って移動すると、ローカル変数パネルの内容も更新され、選択した関数を実行したときのステータスが表示されることに注意してください。

呼び出しスタックを使用して問題のある機能を見つける方法:
  1. 表示するスタック上の関数の名前をクリックします。
  2. ローカル変数は、選択したコンテキストにある値に更新されることに注意してください。
  3. ステップボタンを使用すると、スタックの別の部分にいる場合でも、ブレークポイントのポイントから移動することに注意してください。

makeElementを選択すると、コードの別の部分に移動します。
for ( var prop in attributes) {
el.setAttribute(prop, attributes[prop]);
}


setAttribute呼び出しが表示されます。 ローカル変数パネルは、「prop」変数の値が実際に「クローン」であることを示しています。 変数propはfor ... inループで定義されます。 これは、これが「属性」オブジェクトのプロパティの1つであることを示しています。 このオブジェクトは、2番目のパラメーターによって関数に渡されます。 スタックを1レベル上に移動すると、次のコードが表示されます。
var form = makeElement('form', { action:'/login', method:'post', name:'loginform' } , document .body);

メソッドの2番目のパラメーターは太字で強調表示されています-このオブジェクトにはcloneプロパティがありません。 それはどこから来たのですか?

makeElement関数に戻って、属性変数とその「クローン」プロパティを詳しく見てみましょう。 このプロパティの値関数をクリックすると、割り当てられている場所に移動できます。青で強調表示されます


11:Firebugは、クローンプロパティが定義された場所を示します。

そのため、4番目のエラーの原因を見つけました:Object.prototypeを使用してcloneメソッドがすべてのオブジェクトに追加されます。 オブジェクトのループのfor ...では、Object.prototypeで設定したすべてのプロパティが表示されるため、このアプローチは悪い習慣と見なされます。 これは非常に微妙なエラーにつながる可能性があります。

このエラーを修正するには、次の例に示すように、オブジェクトのプロトタイプからオブジェクト自体にcloneメソッドを直接移動し、obj.clone()へのすべての呼び出しをObject.clone(obj)に置き換えます。
// ,
Object.prototype.clone = function () {
var obj = {};
for ( var prop in this ) {
obj[prop] = this [prop];
}
return obj;
}
// . clone():
var myObj1 = { 'id' : '1' };
var myObj2 = myObj1.clone();

次のように改善してください:
Object.clone = function (originalObject) {
var obj = {};
for ( var prop in originalObject) {
obj[prop] = originalObject[prop];
}
return obj;
}
// . clone():
var myObj1 = { 'id' : '1' };
var myObj2 = Object.clone(myObj1);

* All source code was highlighted with Source Code Highlighter .


この記事の一部を理解したり使用したりするのに問題がある場合は、心配しないでください! 基本をマスターすることで、あなたはよりプロフェッショナルな開発者になり、最終的には使用するための独自の技術セットを開発します。

注釈

1 Safariでデバッグ/開発メニューを有効にするには、[編集]-> [設定]-> [詳細]に移動し、[開発]メニューを有効にします。その後、ブラウザメニューに[開発]項目が表示されます。 ))。
2ロシア語の場合、これは完全に関連するわけではなく、コードは機能します。 FFでは、コードは「ru」のように見えます。 しかし、これはロシア人が1人、ノルウェー人が2人いるからです。 他の欠点とは別に、コードを他の言語で使用する場合、これを行うべきではありません

Source: https://habr.com/ru/post/J51354/


All Articles