script タグ を document.write したときの実行順序【JavaScript】

April 21, 2015

JavaScript の document.write で動的に書き出した scriptタグ 内の JavaScript の実行順序を調べました。

JavaScript document.write script tag Execution Order

以下の通りマークアップ+コーディングしたとき、アルファベット a~k がどのように出力されるか実験しました。

検証コード

HTML

<!doctype html>
<html>
<head></head>
<body>

a

<script>
document.write('<scr' + 'ipt>document.write("b");<\/scr' + 'ipt>');
document.write('c');
</script>

d

<script>
document.write('<scr' + 'ipt>var E = "e";<\/scr' + 'ipt>');
document.write(E);
</script>

f

<script>
document.write('<scr' + 'ipt src="g.js"><\/scr' + 'ipt>');
document.write('h');
</script>

i

<script>
document.write('<scr' + 'ipt src="j.js"><\/scr' + 'ipt>');
document.write(J);
</script>

k

</body>
</html>

g.js

document.write('g');

j.js

document.write('<scr' + 'ipt>var J = "j"<\/scr' + 'ipt>');

ソース コードだけで正確に予測できる人は いないと思う。尚、Chrome(42.0.2311.90)、IE11、FireFox(37.0.1)の各主要ブラウザで表示結果は同じでした。

結果

a bc d e f gh i k

j だけ出力されないという結果。あとはアルファベット順に出力されます。iframe でも貼っておくのでお使いのブラウザでの結果をご確認ください。

iframe

動的に生成された HTML

ページの HTML が最終的にどうなっているか見てみます。これがすべての謎を解く鍵だ。

<!doctype html>
<html><head></head>
<body>

a

<script>
document.write('<scr' + 'ipt>document.write("b");<\/scr' + 'ipt>');
document.write('c');
</script><script>document.write("b");</script>bc

d

<script>
document.write('<scr' + 'ipt>var E = "e";<\/scr' + 'ipt>');
document.write(E);
</script><script>var E = "e";</script>e

f

<script>
document.write('<scr' + 'ipt src="g.js"><\/scr' + 'ipt>');
document.write('h');
</script><script src="g.js"></script>gh

i

<script>
document.write('<scr' + 'ipt src="j.js"><\/scr' + 'ipt>');
document.write(J);
</script><script src="j.js"></script><script>var J = "j"</script>

k

</body></html>

この最終的な HTML の記述順序で JavaScript が実行されたわけではないのが ややこしい。

7~10行目の b~c についていうと、8行目の document.write で 10行目の script タグが書き出され、それがすぐに評価(実行)されたあと、9行目の document.write(‘c’) が実行されるため、結局はアルファベット順通り、bc という出力になります(10行目末尾)。

e も同じ理屈で正しく出力されます。

gh も同じ理屈ですね。外部スクリプトも同期実行なので、ちゃんとアルファベット順になります。

j が問題だ。実験の結果からすると、documento.write で script タグの書き出しをネストした場合同期実行されるのは入れ子になっている親子まで、という動作。でも孫の var J = “j” はちゃんと実行されていて、例えば k のあとに document.write(J) すると j が出力されます。

すべての主要ブラウザで同じ結果ということは、仕様通りの動作ぽい。でも裏付けを取るのはやめました。筆者には必要ない。どうしても真実を探求したい人は、以下のページがヒントになるかも知れない(筆者注:ならないかも知れない)。

筆者は恥ずかしながら、WHATWG の存在を始めて知りました。Web って色んなものから できているんですね。

ちなみに、document.write するスクリプトは色んな人が「やめろ」いや もとい「お願いだから やめてくれ」と言っています。この闇を振り払えるのは、あなた達のような若いエンジニアだけだ。頼んだぞ!

コメントを残す

メールアドレスが公開されることはありません。