document.writeとストリーム

※1157010937の回答の1/4

document.write(以降、writelnも含む)は、
読み込んでいるファイルのストリームの最後に内容を書き出します。

静的なdocument.writeの場合


<html>
<head>
<script type="text/javascript"><!--



var x = 10;



document.write("x = 20;");



alert(x);



//--></script>
</head>
<body>
</body>
</html>
を読み込むとき、alert(x);の直前にx = 20;を書き出すのではありません

このJavaScriptが実行されるとき、HTMLファイルはどこまで読み込まれているかというと、


<html>
<head>
<script type="text/javascript"><!--



var x = 10;



document.write("x = 20;");



alert(x);



//--></script>
までですので、この最後尾にx = 20;が追加され、

<html>
<head>
<script type="text/javascript"><!--



var x = 10;



document.write("x = 20;");



alert(x);



//--></script>x = 20;
</head>
<body>
</body>
</html>
と認識されます。

このとき、ストリームは開いていて、つまりファイルは読み込み中で、その最後尾に追加するのがポイントです。

動的なdocument.writeの場合

では、document.writeで書き出したソースの中にdocument.writeがあったら、それはどうなるのでしょうか?

まずは、最初に書き出したソースに直接document.writeがあった場合


<html>
<head>
<script type="text/javascript"><!--



function dispTime() {
  var str = '<div>現在の時刻は、';
  str += '<script type="text/javascript"><!--\r\n';
  str += 'document.write)((new Date())(.toLocaleString());\r\n';
  str += '/' + '/--></' + 'script>';
  str += 'です。</div>\r\n';
  for(var i = 0; i < 100; i++) {
    str += 'ゴミ文字列';
  }
  document.write(str);
}



//--></script>
</head>
<body>
<script type="text/javascript"><!--
dispTime();
//--></script>
</body>
</html>
これは意図通りに動作するようです。

次に、最初に書き出したソースで外部JSファイルをロードし、そこでdocument.writeしている場合


【dispTime.js】
document.write)((new Date())(.toLocaleString());

<html>
<head>
<script type="text/javascript"><!--



function dispTime() {
  var str = '<div>現在の時刻は、';
  str += '<script type="text/javascript" src="./dispTime.js" charset="Shift_JIS"></' + 'script>';
  str += 'です。</div>\r\n';
  for(var i = 0; i < 100; i++) {
    str += 'ゴミ文字列';
  }
  document.write(str);
}



//--></script>
</head>
<body>
<script type="text/javascript"><!--
dispTime();
//--></script>
</body>
</html>
意図通りに動作しません。
なぜかというと、日時を書きたいdocument.writeが動く時点では、既にストリームに「ゴミ文字列」がたくさん書き込まれてしまっているので、document.writeしてもその後に書き出すだけになります。
ただし、もともと固定でHTMLファイルに書いてある場合には、ちゃんとJSファイルはscriptタグのところで実行されます。

さらに、document.writeするタイミングを2回に分けても無駄です。


<html>
<head>
<script type="text/javascript"><!--



function dispTime() {
  var str = '<div>現在の時刻は、';
  str += '<script type="text/javascript" src="./dispTime.js" charset="Shift_JIS"></' + 'script>';
  str += 'です。</div>\r\n';
  document.write(str);
  str = '';

  for(var i = 0; i < 100; i++) {
    str += 'ゴミ文字列';
  }
  document.write(str);
}



//--></script>
</head>
<body>
<script type="text/javascript"><!--
dispTime();
//--></script>
</body>
</html>
これはJavaScriptのスレッドが(ページごとに)1本しかないためで、仮に「ゴミ文字列」をdocument.writeする前にdispTime.jsを読み込み完了していても、それらを書いたスクリプトが走っている最中です。
そのスレッドが終わってからdispTime.jsの実行の番となるのです。

※(余談)ということは、個人的には外部ファイルを使わない方があるべき姿ではないような気がします。

ファイル読み込み後のdocument.writeの場合

ファイルのロードが終わってしまうと、ストリームが閉じます。
その後でdocument.writeすると、新たなストリームが開かれます。
ブラウザの動作では、新しい白紙ページに遷移して“読み込み中”となり、そこに書き込まれます。


<html>
<head>
<script type="text/javascript"><!--



function dispTime() {
  var str = '<div>現在の時刻は、';
  str += '<script type="text/javascript" src="./dispTime.js" charset="Shift_JIS"></' + 'script>';
  str += 'です。</div>\r\n';
  for(var i = 0; i < 100; i++) {
    str += 'ゴミ文字列';
  }
  document.write(str);
}



//--></script>
</head>
<body>
<input type="button" onclick="dispTime();" value="document.writeする" />
</body>
</html>

まとめ

つまり、document.writeするときにはdocument.writeする時点でのストリームがどうなっているかを意識しなければなりません。