コードの動作に関する回答を投稿するときは、常に次の 2 つの場所に移動するのが好きです。
- 仕様
- 実装
仕様:
DOM API では、スクリプトを次の順序で実行する必要があることを明示的に指定しています。
要素に src 属性があり、async 属性がなく、「force-async」フラグが設定されていない場合、要素は、できるだけ早く実行されるスクリプト
のリストの最後に追加する必要があります。スクリプト アルゴリズムの準備が開始された時点で、script 要素の Document を使用します。
4.1 スクリプティングから。defer
orasync
属性を持つ前に、この規則の例外のリストを確認してください。これは4.12.1.15で明確に規定されています。
これは理にかなっています。想像してみてください。
//FILE_1.js
var trololo = "Unicorn";
....
// 1 million lines later
trololo = "unicorn";
var message = "Hello World";
//FILE_2.js
alert(message); // if file 1 doesn't execute first, this throws a reference error.
通常は、モジュール ローダーを使用することをお勧めします (スクリプトの挿入と実行を延期し、依存関係を正しく管理します)。
現時点では、BrowserifyやRequireJSなどを使用するのが最善の方法です。将来的には、ECMAScript 6 モジュールを使用できるようになります。
実装:
ええと、あなたはそれについて言及しました、そして私は抵抗できませんでした。したがって、Chromium の点滅ソースを確認すると (WebKit でも同様です):
bool ScriptLoader::prepareScript(const TextPosition& scriptStartPosition,
LegacyTypeSupport supportLegacyTypes)
{
.....
} else if (client->hasSourceAttribute() && // has src attribute
!client->asyncAttributeValue() &&// and no `async` or `defer`
!m_forceAsync // and it was not otherwise forced
) { // - woah, this is just like the spec
m_willExecuteInOrder = true; // tell it to execute in order
contextDocument->scriptRunner()->queueScriptForExecution(this,
m_resource,
ScriptRunner::IN_ORDER_EXECUTION);
素晴らしいので、仕様にあるように、解析された順に追加されていることがソース コードでわかります。
スクリプト ランナーの動作を見てみましょう。
void ScriptRunner::queueScriptForExecution(ScriptLoader* scriptLoader,
ResourcePtr<ScriptResource> resource,
ExecutionType executionType){
.....
// Adds it in the order of execution, as we can see, this just
// adds it to a queue
case IN_ORDER_EXECUTION:
m_scriptsToExecuteInOrder.append(PendingScript(element, resource.get()));
break;
}
そして、タイマーを使用して、準備ができたら (または何も保留されていない場合はすぐに) 1 つずつ起動します。
void ScriptRunner::timerFired(Timer<ScriptRunner>* timer)
{
...
scripts.swap(m_scriptsToExecuteSoon);
for (size_t i = 0; i < size; ++i) {
....
//fire!
toScriptLoaderIfPossible(element.get())->execute(resource);
m_document->decrementLoadEventDelayCount();
}