まず、あなたは間違った仮定をしています。最新の JavaScript はコンパイルされています。V8、SpiderMonkey 、および Nitroなどのエンジンは、JS ソースをホスト プラットフォームのネイティブマシン コードにコンパイルします。
古いエンジンでも、JavaScript は解釈されません。これらはソース コードをバイトコードに変換し、エンジンの仮想マシンが実行します。
Java 言語と .NET 言語の実際の動作は次のとおりです。アプリケーションを「コンパイル」すると、実際にはソース コードがプラットフォームのバイトコード、Java バイトコード、およびCILにそれぞれ変換されます。次に、実行時にJIT コンパイラがバイトコードをマシン コードにコンパイルします。
解釈は非常に遅いため、実際に JavaScript ソース コードを解釈するのは、非常に古くて単純な JS エンジンだけです。
では、JS コンパイルはどのように機能するのでしょうか。最初のフェーズでは、ソース テキストが抽象構文木 (AST)に変換されます。これは、マシンが処理できる形式でコードを表すデータ構造です。概念的には、これは HTML テキストがDOM表現に変換される方法によく似ています。これは、コードが実際に動作するものです。
AST を生成するために、エンジンは raw バイトの入力を処理する必要があります。これは通常、字句解析器によって行われます。lexer は実際にはファイルを「1 行ずつ」読み取るわけではありません。むしろ、言語の構文の規則を使用してソース テキストをトークンに変換し、バイト単位で読み取ります。次にレクサーは、トークンのストリームをパーサーに渡します。パーサーは実際に AST を構築します。パーサーは、トークンが有効なシーケンスを形成することを確認します。
これで、構文エラーによってコードがまったく機能しない理由が明確にわかるはずです。ソース テキストに予期しない文字が含まれていると、エンジンは完全な AST を生成できず、次のフェーズに進むことができません。
エンジンに AST があると、次のようになります。
- インタープリターは、AST から直接命令の実行を開始するだけかもしれません。これは非常に遅いです。
- JS VM 実装は、AST を使用してバイトコードを生成し、バイトコードの実行を開始します。
- コンパイラは AST を使用して、CPU が実行するマシン コードを生成します。
少なくとも、JS の実行は 2 つのフェーズで行われることがわかります。
ただし、実行のフェーズは、例が機能する理由に実際には影響しません。JavaScript プログラムがどのように評価され実行されるかを定義するルールがあるため、これは機能します。ルールは、エンジン自体が実際にソースコードを解釈/コンパイルする方法に影響を与えずに、例が機能しないような方法で簡単に記述できます。
具体的には、JavaScript には一般にホイストと呼ばれる機能があります。巻き上げを理解するには、関数宣言と関数式の違いを理解する必要があります。
簡単に言うと、関数宣言とは、別の場所で呼び出される新しい関数を宣言することです。
function foo() {
}
関数式は、変数の割り当てや引数など、式function
が必要な場所でキーワードを使用する場合です。
var foo = function() { };
$.get('/something', function() { /* callback */ });
JavaScriptでは、宣言が (コンテキストの) ソース テキストのどこにあるかに関係なく、実行コンテキストの先頭で関数宣言(最初のタイプ) を変数名に割り当てることが義務付けられています。実行コンテキストは、スコープとほぼ同等です。簡単に言えば、関数内のコード、または関数内でない場合はスクリプトの一番上です。
これにより、非常に奇妙な動作が発生する可能性があります。
var foo = function() { console.log('bar'); };
function foo() { console.log('baz'); }
foo();
コンソールに何が記録されると思いますか? コードを単純に直線的に読むと、 と思うかもしれませんbaz
。bar
ただし、の宣言が にfoo
代入する式の上に持ち上げられているため、実際には がログに記録されfoo
ます。
結論として:
- JS ソース コードは、行ごとに「読み取られる」ことはありません。
- JS ソース コードは、最新のブラウザーで実際に (本当の意味で) コンパイルされます。
- エンジンは複数のパスでコードをコンパイルします。
- あなたの例の動作は、JavaScript 言語のルールの副産物であり、コンパイルや解釈の方法ではありません。