ホームページ > 記事 > ウェブフロントエンド > JavaScript 実行メカニズムのサンプルコード分析
JavaScript 実行メカニズムのサンプル コード分析
簡単な質問から始めます:<script type="text/javascript"> alert(i); var i = 1; </script>出力結果は未定義です。この現象は「事前解析」と呼ばれます。JavaScript エンジンは var 変数と関数の解析を優先します。意味。事前解析が完了するまでコードは実行されません。ドキュメント ストリームに複数のスクリプト コード セグメント (スクリプト タグで区切られた JS コード、またはインポートされた JS ファイル) が含まれている場合、実行シーケンスは次のとおりです。 ステップ 1、最初のコード セグメントを読み取り、エラーがある場合は、ステップ 2 を実行します。構文エラー (括弧の不一致など) が報告され、ステップ 5
step5. 別のコード セグメントがある場合は、次のコード セグメントを読み取り、step2
step6 を繰り返します。上記の分析で多くの問題はすでに説明できますが、私は常に何かが欠けているように感じていました。たとえば、ステップ 3 の「事前解析」とは正確には何ですか?ステップ 4 では、次の例を見てください:
<script type="text/javascript"> alert(i); // error: i is not defined. i = 1; </script>
本棚の隣で、別世界のような『編集の原理』を開くと、見慣れた、しかし見慣れない空白の中にこんなメモがあった。従来のコンパイル言語の場合、コンパイル手順は字句解析、構文解析、意味チェック、コードの最適化、バイト生成に分かれています。
しかし、インタープリタ型言語の場合は、字句解析と構文解析を通じて構文ツリーが取得された後、解釈と実行を開始できます。
NAME "c" EQUALS NAME "a" MINUS NAME "b" SEMICOLON
上記は単なる例です。 、字句解析を確認してください。
「JavaScript の決定版ガイド」の第 2 章では、ECMA-262 にも記載されている字句構造について説明しています。語彙構造は言語の基礎であり、習得は簡単です。字句解析の実装については、別の研究領域であるため、ここでは説明しません。
自然言語を例えとして使用できます。たとえば、英語の段落を単語ごとに中国語に翻訳すると、大量のトークン ストリームが得られます。理解するのが難しいです。さらに翻訳するには文法解析が必要です。次の図は条件文の構文ツリーです。
構文ツリーを構築する際に、if(a { i = 2; } など) が構築できないことが判明した場合、構文はエラーが報告され、コード ブロック全体の解析を終了します。これは、この記事の冒頭のステップ 2 です。
構文分析を通じて構文ツリーを構築した後でも、翻訳された文があいまいな場合があり、さらなる意味チェックが必要になります。従来の強く型付けされた言語の場合、セマンティック チェックの主な部分は、関数の実パラメータと仮パラメータの型が一致するかどうかなどの型チェックです。弱い型付けの言語では、このステップは利用できない場合があります。 JS を読む時間です)。エンジンの実装には、JS エンジンにセマンティック チェック ステップがあるかどうかわかりません。
上記の分析から、JavaScript エンジンには字句解析と構文が必要であることがわかります。これらのコンパイル手順が完了すると (どの言語にもコンパイル プロセスがありますが、インタープリタ言語はバイナリ コードにコンパイルされません)、コードが実行されます。
上記のコンパイル プロセスについては、記事の冒頭ではまだ詳しく説明できません。「事前解析」の一部として、JavaScript コードの実行プロセスを注意深く調査する必要があります。
実行プロセス
Zhou Aiminこれについては、「JavaScript 言語の本質とプログラミングの実践」の第 2 部で非常に注意深く分析しています。以下に私の洞察をいくつか示します。
コンパイルを通じて、JavaScript コードは構文ツリーに変換されます。
さらに実行するには、JavaScript のスコープの仕組みを理解する必要があります。簡単に言えば、JavaScript 変数のスコープは定義時に決定されます。つまり、字句スコープはソース コードに依存し、コンパイラは静的解析を通じてそれを決定できるため、字句スコープは静的スコープとも呼ばれます。ただし、セマンティクスに注意する必要があります。 with と eval は静的技術だけでは実現できません。実際、JS のスコープ機構はレキシカルスコープに非常に近いとしか言えません。
lexical scope是JS的作用域机制,还需要理解它的实现方法,这就是作用域链(scope chain)。scope chain是一个name lookup机制,首先在当前执行环境的scriptObject中寻找,没找到,则顺着upvalue到父级scriptObject中寻找,一直 lookup到全局调用对象(global object)。
当一个函数实例执行时,会创建或关联到一个闭包(closure)。 scriptObject用来静态保存与函数相关的变量表,closure则在执行期动态保存这些变量表及其运行值。closure的生命周期有可能比函数实例长。函数实例在活动引用为空后会自动销毁,closure则要等要数据引用为空后,由JS引擎回收(有些情况下不会自动回收,就导致了内存泄漏)。
别被上面的一堆名词吓住,一旦理解了执行环境、调用对象、闭包、词法作用域、作用域链这些概念,JS语言的很多现象都能迎刃而解。
至此,对于文章开头部分的疑问,可以解释得很清楚了:
step3中所谓的“预解析”,其实是在step2的语法分析阶段完成,并存储在语法树中。当执行到函数实例时,会将varDelcs和funcDecls从语法树中复制到执行环境的scriptObject上。
step4中,未定义变量意味着在scriptObject的变量表中找不到,JS引擎会沿着scriptObject的upvalue往上寻找,如果都没找到,对于写操作i = 1; 最后就会等价为 window.i = 1; 给window对象新增了一个属性。对于读操作,如果一直追溯到全局执行环境的scriptObject上都找不到,就会产生运行期错误。
理解后,雾散花开,天空一片晴朗。
最后,留个问题给大家:
<script type="text/javascript"> var arg = 1; function foo(arg) { alert(arg); var arg = 2; } foo(3); </script>
请问alert的输出是什么?
以上がJavaScript 実行メカニズムのサンプルコード分析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。