スコープについて

以下の記事を参考にスコープについて、まとめます。

Compiler Theory

JSは実はコンパイル言語ということができます。ただ他のコンパイル言語のように、完全なものというのではないので、コンパイル機能をシステムに移植できるわけではありません。

そもそも、コンパイルは次のような手順で遂行されています。

  1. 文字や数字を分割して、意味のある塊(token)にする。
  2. トークンの配置を要素ごとにみる。つまり、言語の文法構造を表したelement treeに変換する(Abstract Syntax Tree)。
  3. ASTの処理を行い、実行可能なコードへと変換する。
var a = 2

上のstatementの場合

  • var → VariableDeclaration
  • a → Identifier 指し示すものです。値がaになります。
  • = → AssignmentExpression
  • 2 → NumericLiteral

JS engineでは、通常のコンパイル手順より複雑な方法で行われている。

まず、JSはコードのビルドの前に、コンパイルを行いません。コンパイルはコードの実行の、直後(マイクロセカンド秒)で行われます。

スコープについて

まず、基本的な用語について確認する。

  1. Engine コンパイル動作を行うJSのプログラム
  2. Compiler 要素の分析、コードの生成を行う
  3. Scope 実行中のコードからどのように、変数にアクセルできるかのルールを決まる、変数の管理をする

また今度は、JSのコンパイラーがどのように、動作しているのかの手順が以下の通りになります。

  • 変数を見た時、スコープにその変数が、そのスコープ内で宣言されていないか確認。もし既にある場合、二つ目の変数は無視。
  • コンパイラーはコードをEngine用に生成。エンジンはスコープに変数のアクセス方法を確認。アクセス可能なら、その変数を使用。

つまり、同じスコープ内で同じ名前の変数を宣言した時、後にある変数が採用、はじめのは無視されます。

エンジンもコンパイラーも、まずスコープにその変数の状態を確認してから作業しているということですね。

またコンパイラーは、エンジンがコードの実行と同時に、変数の管理(宣言や値の定義)を行っています。

RHS / LHS について

Engineがassignmentをする時、どこを参照するかをによって、RHS/LHSに分けられます。

例えば以下の例が挙げられます。

function foo(a) {
var b = a;
return a + b;
}

var c = foo( 2 );

この場合、

LHSは

  • var cのfoo関数の代入
  • 引数aに2を代入
  • 変数bにaを代入

RHSは

  • foo(2)
  • =a
  • a+
  • b

LHSはどの変数にアサインするのか。RHSはその変数はどんな値を持っているか。それぞれの目的によって、参照の種類が変わるということです。

Nested Scope

Nested Scopeの仕組みはかなり、シンプルです。

  • エンジンは現在のスコープ内でまず変数を探します(実際は、そのスコープが探してエンジンに渡しています)。
  • 変数がスコープ内にいない場合、外側のスコープで探します。これを、globalスコープにたどり着くまで、または変数を見つけるまで繰り返します。

最終的に変数が見つからない場合は、エンジンはReferenceErrorを投げます。

また変数が見つかっても、結果に対して理にそっていない(null , undefinedなど)参照をした場合、TypeErrorを投げます。

感想

JSのコンパイル手順を見て、スコープが変数へのアクセス全般を管理している事が理解できました。

Like what you read? Give Tuyoshi Akiyama a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.