ホームページ  >  記事  >  ウェブフロントエンド  >  JavaScriptの実行順序を詳しく紹介_基礎知識

JavaScriptの実行順序を詳しく紹介_基礎知識

WBOY
WBOYオリジナル
2016-05-16 17:10:501418ブラウズ

以前、JavaScript エンジンの解析メカニズムから JavaScript の動作原理を調べました。以下では、ページ内の JavaScript コードの実行順序を説明するために、より鮮明な例を使用します。 JavaScript エンジンの動作メカニズムが基礎的な動作に属しているため、比較的奥深い場合、JavaScript コードの実行順序はより鮮明になります。これは、JavaScript コードの実行順序を直感的に感じることができるためです。は比較的複雑であるため、JavaScript 言語について詳しく説明する前に、JavaScript 言語のプロファイルを作成することも必要です。
1.1 HTML ドキュメント フローの順序で JavaScript コードを実行する
まず、読者はブラウザでの HTML ドキュメントの解析プロセスが次のようなものであることを知っておく必要があります。ブラウザはドキュメント フローに従います。上から下へ 以下は、ページの構造と情報を段階的に分析したものです。埋め込みスクリプトとしての JavaScript コードも HTML ドキュメントのコンポーネントとしてカウントされる必要があるため、読み込み中の JavaScript コードの実行順序もスクリプト タグ <script> の出現順序に基づいて決定されます。たとえば、以下のドキュメント ページを参照すると、コードが上から下まで段階的に解析されていることがわかります。 </script>

コードをコピー コードは次のとおりです。

<script><br>alert( "トップ スクリプト ");<br></script>

<script><br>alert("ヘッド スクリプト");<br></script&gt ;<BR><title></title><br></head><br><body><br><script><br>alert("ページスクリプト");<br>&lt ;/ script><br></body></html><br><script><br>alert("ボトムスクリプト");<br></script>

外部 JavaScript ファイルのスクリプトがスクリプト タグ <script> の src 属性を通じてインポートされる場合、そのスクリプトもそのステートメントが出現する順序で実行され、実行プロセスはドキュメントの読み込みの一部となります。外部JavaScriptファイルなので実行が遅れることはありません。たとえば、上記のドキュメントのヘッド領域とボディ領域にあるスクリプトを外部 JavaScript ファイルに移動し、src 属性を通じてインポートします。ページドキュメントのプレビューを続けると、同じ実行順序が表示されます。 <br></p> <div class="codetitle"> <span><a style="CURSOR: pointer" data="39983" class="copybut" id="copybut39983" onclick="doCopy('code39983')"><u>コードをコピー</u></a></span> コードは次のとおりです:</div> <div class="codebody" id="code39983"> <br><script> <p>alert("トップスクリプト");</p> <p></script>

<スクリプト src="http://www.jb51.net/head.js">

<本体>

<スクリプト src="http://www.jb51.net/body.js">

alert("ボトムスクリプト");

1.2 プリコンパイルと実行順序の関係

JavaScript では、関数は Javascript の最初のタイプです。関数を記述するとき、実際には関数型のエンティティを作成しているだけです。
フォームで書くのと同じように:

コードをコピーします コードは次のとおりです:

functionHello()
{
alert("Hello");
}
Hello();
varHello = function()
{
alert( "こんにちは") ;
}
こんにちは();

実際には、それらはすべて同じです。 しかし、機能を変更すると、非常に奇妙な問題が発生します。
コードをコピー コードは次のとおりです:


functionHello() {
Hello(); ;/script>


次のような結果が表示されます: Hello World が 2 回続けて出力されます。
私たちが想像していた Hello and Hello World ではありません。
これは、JavaScript が完全に解釈されて順番に実行されるわけではありませんが、プリコンパイル プロセス中に、定義された関数が最初に実行され、すべての関数が最初に実行されるためです。プログラムの実行効率を向上させるために作成され、デフォルト値は未定義です。
つまり、上記のコードは実際には JS エンジンによって次の形式にプリコンパイルされています:
コードをコピー コードは次のとおりです:

) {
上記のコードから、関数がデータおよび変数でもあることが明確にわかります。 . 「関数」に値を代入(再割り当て)することもできます。
もちろん、この状況を防ぐために、次のこともできます:




コードをコピーします


コードは次のとおりです。



functionHello() {
alert("Hello"); Hello(); ; functionHello() { alert("Hello World") } >
このようにプログラムは 2 つのセクションに分割されており、JS エンジンはそれらを結合しません。

JavaScript エンジンはスクリプトを解析する際、プリコンパイル中に宣言されたすべての変数と関数を処理します。

次の操作を行います:

1. 実行前に「プリコンパイル」と同様の操作が実行されます。まず、現在の実行環境でアクティブなオブジェクトが作成され、var で宣言された変数がアクティブなオブジェクトの属性として設定されます。今回はこれらの変数の割り当て値はすべて未定義であり、関数として定義されている関数もアクティブなオブジェクトのプロパティとして追加され、それらの値は関数の定義とまったく同じです。

2. 解釈および実行フェーズ中に、変数を解析する必要がある場合、変数が見つからず、実行環境の所有者がプロトタイプを持っている場合は、まず現在の実行環境のアクティブなオブジェクトから検索されます。属性の場合はプロトタイプ チェーンから検索され、それ以外の場合はスコープ チェーンに従って検索されます。 var a = ... などのステートメントに遭遇すると、対応する変数に値が割り当てられます (注: 変数の割り当ては解釈および実行フェーズ中に完了します。これより前に変数が使用された場合、その値は未定義)。したがって、JavaScript インタープリターが次のスクリプトを実行してもエラーは報告されないように見えます:





コードをコピーします

コードは次のとおりです。

alert(a);

var a =1;

alert(a); // 戻り値 1
変数宣言はプリコンパイル中に処理されるため、実行中にすべてのコードから参照できます。ただし、上記のコードを実行すると、プロンプトされる値が 1 ではなく、未定義であることもわかります。これは、変数の初期化プロセスがプリコンパイルではなく実行中に発生するためです。実行中に、JavaScript インタープリターはコードを順番に解析します。コードの前の行で変数に値が割り当てられていない場合、JavaScript インタープリターはデフォルト値の unknown を使用します。変数 a には 2 行目で値が割り当てられているため、コードの 3 行目では変数 a の値が未定義ではなく 1 であることが示されます。

同様に、次の例では、関数が宣言される前に関数を呼び出すことは正当であり、正しく解析できるため、戻り値は 1 になります。

コードをコピーします コードは次のとおりです。

f(); 関数 f(){

アラート(1);

}

ただし、関数が次のように定義されている場合、JavaScript インタプリタにより構文エラーが表示されます。

コードをコピーします コードは次のとおりです。
f();
var f = function(){

アラート(1);

}


これは、上記の例で定義された関数は変数 f に値としてのみ割り当てられるため、プリコンパイル期間中、JavaScript インタープリターは変数 f の宣言と変数 f の値のみを処理できるためです。実行まで待つことしかできません 代入を順番に実行すると、当然構文エラーが発生し、オブジェクト f が見つからないというメッセージが表示されます。

いくつかの例を見てみましょう:




変数と関数の宣言はドキュメント内のどこにでも置くことができますが、すべての JavaScript コードの前にグローバル変数と関数を宣言し、変数を初期化して代入することをお勧めします。関数内では、変数は最初に宣言されてから参照されます。

1.3 JavaScript コードをブロックで実行する

いわゆるコード ブロックは、<script> タグで区切られたコード セグメントです。たとえば、以下の 2 つの <script> タグは JavaScript コードの 2 つのブロックを表します。 </script>

コードをコピー コードは次のとおりです:<script></div> <div class="codebody" id="code83113">// JavaScript コードブロック 1<br> var a =1;<p> </p></script>

// JavaScript コード ブロック 2

関数 f(){

アラート(1);

}

JavaScript インタープリターはスクリプトを実行する際、ブロック単位で実行します。平たく言えば、ブラウザが HTML ドキュメント ストリームを解析するときに <script> タグを検出すると、JavaScript インタプリタはコード ブロックがロードされるまで待機し、コード ブロックをプリコンパイルしてから実行します。実行後、ブラウザーは以下の HTML ドキュメント ストリームの解析を続け、JavaScript インタープリターはコードの次のブロックを処理する準備が整います。 </p> <p>JavaScript はブロック単位で実行されるため、後続のブロックで宣言された変数や関数を JavaScript ブロック内で呼び出すと、構文エラーが表示されます。たとえば、JavaScript インタプリタが次のコードを実行すると、変数 a が未定義でオブジェクト f が見つからないことを示す構文エラーが表示されます。 </p> <p></p> <div class="codetitle"> <span><a style="CURSOR: pointer" data="35451" class="copybut" id="copybut35451" onclick="doCopy('code35451')"><u>コードをコピー</u></a></span> コードは次のとおりです:</div> <div class="codebody" id="code35451"> <br><script> <p>// JavaScript コードブロック 1</p> <p>アラート(a);</p> <p>f();</p> <p></script>

// JavaScript コード ブロック 2

var a =1;

関数 f(){

アラート(1);

}


JavaScript はブロック内で実行されますが、異なるブロックは同じグローバル スコープに属します。これは、ブロック間の変数や関数を共有できることを意味します。

1.4 イベントメカニズムを使用して JavaScript の実行順序を変更する

JavaScript はコードをブロック単位で処理し、HTML ドキュメント フローの解析順序に従うため、上記の例ではこのような構文エラーが発生します。ただし、ドキュメント ストリームがロードされると、再度アクセスしてもこのようなエラーは発生しません。たとえば、コードの 2 番目のブロックの変数と関数にアクセスするコードがページ初期化イベント関数に配置されている場合、構文エラーは発生しません。

コードをコピー コードは次のとおりです:

<script> <p>// JavaScript コードブロック 1</p> <p>window.onload = function(){ // ページ初期化イベント ハンドラー関数</p> <p> アラート(a);</p> <p> f();</p> <p>}</p> <p></script>

// JavaScript コード ブロック 2

var a =1;

関数 f(){

アラート(1);

}


セキュリティ上の理由から、通常、JavaScript コードの実行はページの初期化後にのみ許可されます。これにより、JavaScript の実行に対するネットワーク速度の影響が回避され、HTML ドキュメント フローによって引き起こされる JavaScript の実行の制限も回避されます。

注意

ページ内に複数の windows.onload イベント ハンドラーがある場合、最後のものだけが有効です。この問題を解決するには、すべてのスクリプトまたは呼び出し関数を同じ onload イベント ハンドラーに含めることができます。次に例を示します。 >

コードをコピー コードは次のとおりです。
window.onload = function(){
f1();

f2();

f3();

}


このように、onload イベントハンドラー内で関数の呼び出し順序を調整するだけで、関数の実行順序を変更できます。

ページ初期化イベントに加えて、マウス イベント、キーボード イベント、クロック トリガーなどのさまざまなインタラクティブ イベントを通じて JavaScript コードの実行順序を変更することもできます。詳細な説明については、第 14 章を参照してください。

1.5 JavaScript 出力スクリプトの実行順序

JavaScript 開発では、JavaScript スクリプトを出力するためにドキュメント オブジェクトの write() メソッドがよく使用されます。では、これらの動的出力スクリプトはどのように実行されるのでしょうか?例:

コードをコピー コードは次のとおりです。
document.write('');



上記のコードを実行すると、次のことがわかります。 document.write() メソッドは、まずスクリプトが配置されているドキュメントの場所に出力スクリプト文字列を書き込み、ブラウザはドキュメントのコンテンツを解析した後も解析を続けます。 document.write() が見つかりました。 document.write() によって出力されたコンテンツは、次の HTML ドキュメントを解析するために解析されます。つまり、JavaScript スクリプトが出力したコード列は、出力直後に実行されます。

document.write() メソッドを使用して出力される JavaScript スクリプト文字列は、同じく出力される <script> タグ内に配置する必要があることに注意してください。そうしないと、JavaScript インタプリタはこれらの正当な JavaScript コードを認識せず、通常のコードとして扱います。文字列はページドキュメントに表示されます。たとえば、次のコードは JavaScript コードを実行する代わりに表示します。 </p> <p></p> <div class="codetitle"> <span><a style="CURSOR: pointer" data="94458" class="copybut" id="copybut94458" onclick="doCopy('code94458')"><u>コードをコピー</u></a></span> コードは次のとおりです。</div> <div class="codebody" id="code94458"> <br>document.write('f(); '); <p>document.write('function f(){');</p> <p>document.write('alert(1);');</p> <p>document.write(');');<br></p> </div> <p>ただし、document.write() メソッドを介してスクリプトを出力および実行する場合には、一定のリスクが伴います。これは、JavaScript エンジンによってスクリプトが異なる順序で実行され、ブラウザによって解析にバグが発生する可能性があるためです。 </p> <p>Ø 問題 1: document.write() メソッドを通じてインポートされた外部 JavaScript ファイルで宣言された変数または関数が見つかりません。たとえば、以下のサンプルコードを見てください。 </p> <p></p> <div class="codetitle"> <span><a style="CURSOR: pointer" data="27974" class="copybut" id="copybut27974" onclick="doCopy('code27974')"><u>コードをコピー</u></a></span> コードは次のとおりです。</div> <div class="codebody" id="code27974"> <br>document.write('<script type ="text /javascript" src="http://www.jb51.net/test.js"> <p></script>');

document.write('');

Alert (n 1); // すべてのブラウザは変数 n が見つからないことを通知します

外部 JavaScript ファイル (test.js) のコードは次のとおりです。

コードをコピーします コードは次のとおりです

var n = 1;

別のブラウザでテストすると、構文エラーが見つかり、変数 n が見つかりません。つまり、JavaScript コード ブロック内のこのコード ブロックの document.write() メソッドを使用して、スクリプト出力にインポートされた外部 JavaScript ファイルに含まれる変数にアクセスすると、構文エラーが表示されます。同時に、IE ブラウザの場合、スクリプトだけでなく出力スクリプトでも、外部 JavaScript ファイルにインポートされた出力変数が見つからないというメッセージが表示されます (式は少し長くて複雑です)理解できない読者は、上記のコードを実行してみると理解できます)。

Ø 質問 2: JavaScript エンジンが異なると、出力外部インポート スクリプトの実行順序が若干異なります。たとえば、以下のサンプルコードを見てください。

コードをコピー コードは次のとおりです:

');

document.write('');



外部JavaScriptファイル(test1.js)のコードは以下のとおりです。
コードをコピーします コードは次のとおりです:

var n = 1;

アラート(n);

IE ブラウザでの実行シーケンスを図 1-6 に示します。

JavaScriptの実行順序を詳しく紹介_基礎知識

図 1-6 IE 7 ブラウザの実行シーケンスと表示される構文エラー

DOM 標準に準拠したブラウザでの実行順序は、IE ブラウザでの実行順序とは異なり、構文エラーはありません。図 1-7 は、Firefox 3.0 ブラウザでの実行順序を示しています。

JavaScriptの実行順序を詳しく紹介_基礎知識

図 1-7 Firefox 3 ブラウザの実行シーケンスと表示される構文エラー

さまざまなブラウザでのさまざまな実行順序と考えられるバグを解決します。出力スクリプトを使用してインポートされたすべての外部ファイルを独立したコード ブロックに配置できるため、上で紹介した JavaScript コード ブロックの実行順序に従ってこの問題を回避できます。たとえば、上記の例の場合、次のように設計できます:

コードをコピー コードは次のとおりです:

');

');

alert(n 3) // ヒント 4

このように、上記のコードは異なるブラウザで順番に実行でき、出力順序は 1、2、3、4、5 になります。問題の理由は、インポートされた出力スクリプトと現在の JavaScript コード ブロック間の矛盾です。別々に出力した場合は競合しません。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
前の記事:JavaScriptでプライベート属性を持つクラスの書き方(1)_javascriptスキル次の記事:JavaScriptでプライベート属性を持つクラスの書き方(1)_javascriptスキル

関連記事

続きを見る